From 6e5b951fe5b04673fe26350d5dfc16c00b89b50b Mon Sep 17 00:00:00 2001 From: Jan Vonka Date: Mon, 18 Mar 2013 09:52:08 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20BRANCHES/DEV/CONV=5FHEAD=20to=20HEAD:?= =?UTF-8?q?=20=20=20=2048055:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20B?= =?UTF-8?q?RANCHES/DEV/CONV=5FHEAD:=20=20=20=20=20=20=20=20=20=2046833:=20?= =?UTF-8?q?Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20Merged=20BRANCHES/DEV/TH?= =?UTF-8?q?OR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2030799:=20THOR-172:=20Switch=20Tenant=20via?= =?UTF-8?q?=20public=20API=20=20=20=20=20=20=20=20=20=2046836:=20Merged=20?= =?UTF-8?q?BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20Merged=20BRANCHES/DEV/THOR1=20to?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2030853:=20Initial=20implementation=20of=20THOR-209.?= =?UTF-8?q?=20Webscript=20to=20get=20invitation/invitee=20status.=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2030855:=20More=20on=20?= =?UTF-8?q?THOR-209.=20Added=20siteTenantTitle=20to=20the=20webscript=20re?= =?UTF-8?q?sponse.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030858?= =?UTF-8?q?:=20Apply=20generated=20cloud=20license=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2030859:=20Miscellaneous=20tidy-ups=20?= =?UTF-8?q?and=20refactorings,=20additional=20documentation=20and=20some?= =?UTF-8?q?=20webscript=20JSON=20additions.=20All=20as=20part=20of=20THOR-?= =?UTF-8?q?209.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030860:?= =?UTF-8?q?=20Miscellaneous=20doc=20improvements=20around=20the=20MT/Activ?= =?UTF-8?q?iti=20workarounds.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2030861:=20Removing=20unnecessary=20TenantUtil.runas=20in?= =?UTF-8?q?=20test=20code.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2030863:=20THOR-204.=20Dev=20mode=20option=20to=20send=20invit?= =?UTF-8?q?e/sign-up=20emails=20to=20spring-injected=20address.=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2030865:=20Temporarily?= =?UTF-8?q?=20disable=20subscriptions=20(followers)=20-=20pending=20ALF-99?= =?UTF-8?q?57=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030866:=20T?= =?UTF-8?q?HOR-175:=20Set=20and=20enforce=20file=20space=20quota=20for=20t?= =?UTF-8?q?enant=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030868:?= =?UTF-8?q?=20Deleted=20obsolete/empty=20dir=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2030869:=20THOR-210:=20disable=20jobs=20that?= =?UTF-8?q?=20are=20not=20used/required=20(eg.=20AVM=20orphan=20reaper)=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030870:=20THOR-2?= =?UTF-8?q?09.=20Have=20fixed=20up=20issue=20with=20getting=20properties?= =?UTF-8?q?=20from=20completed=20workflow=20instances.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20Changed=20invitati?= =?UTF-8?q?on=20to=20use=20pathInstanceId=20instead=20of=20taskId=20as=20t?= =?UTF-8?q?he=20'id'=20for=20these=20workflows.=20Now=20consistent=20with?= =?UTF-8?q?=20signup.=20Commented=20in=20the=20test=20that=20calls=20invit?= =?UTF-8?q?ee-status.get=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2030871:=20THOR-209.=20Adding=20the=20inviteeIsActivated=20val?= =?UTF-8?q?ue=20to=20the=20webscript=20response.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2030872:=20THOR-204.=20When=20emails?= =?UTF-8?q?=20are=20sent=20to=20the=20dev-only,=20spring-injected=20email?= =?UTF-8?q?=20address,=20the=20subject=20is=20now=20prefixed=20with=20the?= =?UTF-8?q?=20orig=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030879?= =?UTF-8?q?:=20THOR-209.=20Making=20sure=20inviteeIsActivated=20is=20prese?= =?UTF-8?q?nt=20for=20both=20in-flight=20and=20completed=20workflows.=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030883:=20Resolv?= =?UTF-8?q?e=20THOR-212=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?30895:=20THOR-172:=20Switch=20Tenant=20via=20public=20API=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030896:=20THOR-209.?= =?UTF-8?q?=20Renaming=20some=20files=20so=20that=20they=20refer=20to=20in?= =?UTF-8?q?vitation=20status=20rather=20than=20invitee=20status.=20Also=20?= =?UTF-8?q?added=20some=20documentation=20to=20make=20this=20dicstinction?= =?UTF-8?q?=20clearer.=20This=20is=20not=20a=20general=20purpose=20script?= =?UTF-8?q?=20to=20get=20the=20status=20of=20an=20invitee=20to=20a=20site.?= =?UTF-8?q?=20It=20is=20only=20for=20checking=20if=20a=20particular=20invi?= =?UTF-8?q?tation=20workflow=20is=20complete=20and=20then=20getting=20some?= =?UTF-8?q?=20additional=20state=20data.=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2030897:=20THOR-175:=20Set=20and=20enforce=20fi?= =?UTF-8?q?le=20space=20quota=20for=20tenant=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2030900:=20Changing=20invitation-status=20we?= =?UTF-8?q?bscript=20to=20auth=3Dnone;=20runas=3DAdmin=20to=20support=20in?= =?UTF-8?q?vitation=20flow=20of=20exteernal=20users.=20Part=20of=20THOR-20?= =?UTF-8?q?9.=20=20=20=20=20=20=20=20=2046845:=20Merged=20BRANCHES/DEV/CLO?= =?UTF-8?q?UD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLO?= =?UTF-8?q?UD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030967:?= =?UTF-8?q?=20Invite=20&=20signup=20improvemengts=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2030969:=20Share=20Activities=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2030976:=20Remove=20unr?= =?UTF-8?q?eliable=20hosts=20from=20isReachableDomain=20test=20=20=20=2048?= =?UTF-8?q?066:=20Merged=20DEV/CONV=5FV413=20to=20DEV/CONV=5FHEAD=20(RECOR?= =?UTF-8?q?D=20ONLY)=20=20=20=20=20=20=20=20=2046857:=20Merged=20from=20BR?= =?UTF-8?q?ANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2035731:=20Merged=20BRANCHES/DEV/THOR1=20?= =?UTF-8?q?to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031149:=20Initial=20Commit=20of=20Analytics=20Service?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031150:=20Second=20?= =?UTF-8?q?draft=20of=20Analytics=20Service=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031151:=20First=20cut=20of=20forms=20runtime=20su?= =?UTF-8?q?pporting=20balloons=20on=20"blur"=20event=20as=20requested=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031163:=20FORMS=20RUNT?= =?UTF-8?q?IME=20CHECKPOINT=20-=20before=20making=20the=20yellow=20mandato?= =?UTF-8?q?ry=20only=20being=20displayed=20"on=20load=20and=20until=20focu?= =?UTF-8?q?sed"=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031168:=20Refac?= =?UTF-8?q?tored=20Analytics=20Service=20to=20be=20static=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031170:=20Forms=20runtime=20as=20a?= =?UTF-8?q?greed=20in=20meeting=20=20=20=2048067:=20Merged=20DEV/CONV=5FV4?= =?UTF-8?q?13=20to=20DEV/CONV=5FHEAD=20(RECORD=20ONLY)=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2046861:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BR?= =?UTF-8?q?ANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20=20=20=20=20=2035752?= =?UTF-8?q?:=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031220:=20THOR-49.=20I?= =?UTF-8?q?mplementation=20of=20Reset=20Forgotten=20Password=20workflow.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031227:=20(RECORD?= =?UTF-8?q?=20ONLY)=20Fix=20merge=20error=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031237:=20Add=20email=20validation=20to=20registrati?= =?UTF-8?q?on=20and=20invite=20services:=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031239:=20THOR-219:=20Merge=20fix=20(re-disable=20Re?= =?UTF-8?q?po<->SOLR=20ssl=20config)=20=20=20=2048069:=20Merged=20DEV/CONV?= =?UTF-8?q?=5FV413=20to=20DEV/CONV=5FHEAD=20(RECORD=20ONLY)=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2046864:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to?= =?UTF-8?q?=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?35754:=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1?= =?UTF-8?q?:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031240:=20MultiSel?= =?UTF-8?q?ectAutoComplete=20now=20has=20configurable=20validation=20(&=20?= =?UTF-8?q?tooltips)=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031241:=20?= =?UTF-8?q?Tenant=20site=20count=20usage/quota=20-=20exposed=20via=20Accou?= =?UTF-8?q?nt=20API=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031250:=20M?= =?UTF-8?q?ade=20events=20enumerations=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031251:=20Forgot=20to=20add=20AnalyticsEvent=20class=20?= =?UTF-8?q?to=20previous=20commit=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031271:=20Attempt=20at=20fixing=20test=20dependencies=20an?= =?UTF-8?q?d=20remove=20intermittent=20test=20=20=20=2048070:=20Merged=20D?= =?UTF-8?q?EV/CONV=5FV413=20to=20DEV/CONV=5FHEAD=20(NOTE!=20Added=20Tenant?= =?UTF-8?q?Xxxx=20classes=20and=20change=20in=20FormUIGet=20Will=20be=20re?= =?UTF-8?q?moved=20in=20later=20revisions)=20=20=20=20=20=20=20=20=2046911?= =?UTF-8?q?:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CON?= =?UTF-8?q?V=5FV413=20=20=20=20=20=20=20=20=20=20=20=2035757:=20Merged=20B?= =?UTF-8?q?RANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031367:=20Merged=20BRANCHES/DEV/THOR1?= =?UTF-8?q?SURF=20to=20BRANCHES/DEV/THOR1:=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2030971:=20(RECORD=20ONLY)=20Creati?= =?UTF-8?q?ng=20SURF=20update=20branch=20for=20THOR1=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2030979:=20Commit=20initia?= =?UTF-8?q?l=20Surf=20lib=20changes,=20Cloud=20classes=20and=20config=20ov?= =?UTF-8?q?errides=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2030980:=20Add=20custom=20Cloud=20Surf=20authenticator,=20c?= =?UTF-8?q?onnector,=20remote=20store,=20user=20and=20user=20factory=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031015:=20?= =?UTF-8?q?Renamed=20classes=20and=20references=20from=20Cloud=20to=20Tena?= =?UTF-8?q?nt,=20custom=20page=20view,=20page=20view=20resolvers,=20URLMod?= =?UTF-8?q?el=20+=20factory,=20URLHelper=20+=20factory=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031076:=20Successful?= =?UTF-8?q?=20signup=20and=20page=20redirection=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031091:=20Correct=20redirects?= =?UTF-8?q?=20from=20=20and=20/=20URLs=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031098:=20Updated=20Surf=20libs=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031132:=20Ten?= =?UTF-8?q?ant=20specific=20implementation=20of=20PathStoreObjectPersister?= =?UTF-8?q?=20-=20Surf=20modelobject=20cache=20is=20now=20partitioned=20by?= =?UTF-8?q?=20the=20tenant=20name.=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031133:=20Updated=20Surf=20libs=20and=20Jav?= =?UTF-8?q?aDoc=20updates=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031155:=20Initial=20code=20to=20handle=20attempted?= =?UTF-8?q?=20access=20to=20unauthorised=20tenants,=20secondary=20tenants?= =?UTF-8?q?=20added=20to=20TenantUser=20and=20page/activations=20filter=20?= =?UTF-8?q?rule=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031210:=20Fixed=20401=20&=20409=20errors=20on=20remote=20GET/?= =?UTF-8?q?POST=20calls.=20Logout=20redirection=20support.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031229:=20Signup=20?= =?UTF-8?q?and=20invitiation=20completion=20updates=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031242:=20Fixed=20up=20in?= =?UTF-8?q?vitation,=20signup=20and=20tenant=20switching=20problems=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031270:=20?= =?UTF-8?q?Fixed=20forms=20issue=20(can=20now=20create=20folders=20in=20do?= =?UTF-8?q?c=20lib)=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031277:=20THOR-207.=20Invitation=20workflows=20now=20run?= =?UTF-8?q?=20in=20the=20inviter's=20tenant=20rather=20than=20the=20defaul?= =?UTF-8?q?t=20tenant.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20This=20is=20checked=20in=20on=20a=20side-b?= =?UTF-8?q?ranch=20because=20the=20invitation=20email's=20accept/reject=20?= =?UTF-8?q?links=20include=20the=20tenantId=20and=20this=20tenant-aware=20?= =?UTF-8?q?Share=20URL=20is=20not=20yet=20supported=20on=20the=20THOR1=20b?= =?UTF-8?q?ranch.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20I=20removed=20various=20TenantUtil.runAsWork=20c?= =?UTF-8?q?alls=20which=20were=20causing=20the=20workflow=20to=20run=20on?= =?UTF-8?q?=20the=20default=20tenant=20rather=20then=20the=20current=20ten?= =?UTF-8?q?ant.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20SendCloudInvitationEmailDelegate.createInvitatio?= =?UTF-8?q?nUrl=20now=20includes=20the=20tenantId=20in=20the=20Share=20URL?= =?UTF-8?q?=20it=20generates.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20Added=20new=20test=20cases=20at=20th?= =?UTF-8?q?e=20Java=20API=20level.=20(Was=20formerly=20just=20at=20REST=20?= =?UTF-8?q?API=20level).=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031286:=20(RECORD=20ONLY)=20Reset=20solrcore.propert?= =?UTF-8?q?ies=20files=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031297:=20Fixed=20FlashUpload=20problem=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031298:=20Fixed=20a?= =?UTF-8?q?pplication=20context=20only=20login=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031302:=20Fixed=20no=20user=20p?= =?UTF-8?q?rofile=20image=20url=20issue=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031306:=20Updates=20to=20TenantUserFa?= =?UTF-8?q?ctory=20to=20defensively=20handle=20missing=20tenant=20data=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031326:?= =?UTF-8?q?=20Repo=20switch=20tenant=20fixes:=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031356:=20Resolve=20switch=20te?= =?UTF-8?q?nant=20niggles,=20with=20assistance=20from=20Erik:=20=20=20=204?= =?UTF-8?q?8072:=20Merged=20DEV/CONV=5FV413=20to=20DEV/CONV=5FHEAD=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2046934:=20Merged=20from=20BRANCHES/DEV/CLOU?= =?UTF-8?q?D2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2046930:=20Adding=20extension=20point=20to=20forms=20run?= =?UTF-8?q?time's=20FormUIGet=20for=20modifying=20submission=20url=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2046937:=20Merged=20from=20BRANCHES/DEV/CLOU?= =?UTF-8?q?D2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2035762:=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHES/D?= =?UTF-8?q?EV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031408:?= =?UTF-8?q?=20Latest=20Spring=20Surf=20Libs=20(including=20SubComponentEva?= =?UTF-8?q?luator=20param=20tokenization=20fix=20for=20THOR)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031409:=20Tenant=20usage/quota?= =?UTF-8?q?=20->=20person=20count=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031412:=20RM=20module=20cleanup=20&=20almost=20finsihed=20?= =?UTF-8?q?THOR-287=20&=20THOR-288=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031434:=20Fix=20Thor=20Share=20eclipse=20project=20=20=20?= =?UTF-8?q?=2048073:=20CONV:=20Fix=20slingshot=20eclipse=20.classpath=20(a?= =?UTF-8?q?dd=20freemarker=20dep)=20=20=20=2048074:=20Merged=20DEV/CONV=5F?= =?UTF-8?q?V413=20to=20DEV/CONV=5FHEAD=20=20=20=20=20=20=20=20=2046940:=20?= =?UTF-8?q?Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2035766:=20Merged=20BRANCHES/?= =?UTF-8?q?DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031450:=20Additional=20DB=20query=20s?= =?UTF-8?q?upport=20in=20Repo/Core=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031451:=20Tenant=20people=20count=20(internal=20+=20?= =?UTF-8?q?total)=20usage/quota=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031453:=20Fix=20issue=20uploading=20small=20files=20whi?= =?UTF-8?q?ch=20resulted=20in=20zero=20byte=20content=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031456:=20Fix=20paging=20and=20tot?= =?UTF-8?q?al=20count=20(1000+)=20when=20listing=20accounts=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031457:=20THOR-312.=20Additi?= =?UTF-8?q?on=20of=20NETWORK=5FADMINS=20group=20authority.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031461:=20THOR-314.=20I've?= =?UTF-8?q?=20overridden=20people.get=20with=20a=20cloud-specific=20templa?= =?UTF-8?q?te.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20This=20adds=20an=20isExternal=20JSON=20field=20to=20each=20p?= =?UTF-8?q?erson=20object.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031463:=20THOR-316=20webscript=20filter=20on=20people.get=20f?= =?UTF-8?q?or=20isInternal,=20isExternal.=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20Overridden=20people.get.desc.xml?= =?UTF-8?q?=20and=20.js=20in=20the=20cloud=20module=20in=20order=20to=20ad?= =?UTF-8?q?d=20query=20param=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20and=20add=20additional=20filtering.=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031464:=20Base=20work?= =?UTF-8?q?=20for=20THOR-178=20"F5:=20Existing=20user=20has=20forgotten=20?= =?UTF-8?q?password=20and=20needs=20to=20reset=20it"=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031468:=20THOR-321=20Create=20clou?= =?UTF-8?q?d:networkAdmin=20aspect.=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031469:=20THOR-315=20Return=20isNetworkAdmin=20in=20?= =?UTF-8?q?overridden=20people.get=20webscript.=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031470:=20THOR-275:=20Add=20simple=20cac?= =?UTF-8?q?hing=20(for=20PropertyUniqueContext=20-=20used=20via=20Attribut?= =?UTF-8?q?eService)=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20314?= =?UTF-8?q?71:=20THOR-318=20people.get=20has=20new=20cloud=20query=20param?= =?UTF-8?q?eter=20'networkAdmin'=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031477:=20THOR-275:=20temp=20build=20fix=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031479:=20THOR-324=20Demote?= =?UTF-8?q?=20user=20from=20admin.=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20New=20method=20on=20RegistrationService?= =?UTF-8?q?=20to=20demote=20a=20user=20from=20NetworkAdmin=20and=20tests.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031484:=20THO?= =?UTF-8?q?R-319.=20Fixing=20maxResults=20on=20people.get=20when=20interna?= =?UTF-8?q?l/external/admin=20filtering=20is=20applied.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20This=20issue=20is?= =?UTF-8?q?=20not=20really=20resolved,=20but=20I've=20commented=20the=20co?= =?UTF-8?q?de=20to=20illuminate=20the=20issue.=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031485:=20THOR-275:=20fix=20build/test?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031486:=20Wor?= =?UTF-8?q?king=20forgot=20password=20for=20THOR-178=20(problem=20accessin?= =?UTF-8?q?g=20the=20reset-password=20email=20link=20though)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031488:=20THOR-184:=20Dis?= =?UTF-8?q?able=20user=20usages=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031495:=20F156:=20Allow=20super=20system=20admin=20to?= =?UTF-8?q?=20login=20to=20any=20tenant=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031496:=20Fix=20for=20personExists=20since=20h?= =?UTF-8?q?iding=20admin=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031500:=20THOR-178=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031501:=20THOR-329=20Add=20a=20get-reset-password=20sta?= =?UTF-8?q?tus=20webscript.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031503:=20Finished=20forgot=20password=20flow=20THOR-178?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031507:=20THO?= =?UTF-8?q?R-328:=20add=20fixed=20adjustment=20(for=20people=20usage)=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031508:=20Back?= =?UTF-8?q?=20out=20some=20of=20the=20hidden=20admin=20changes=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031509:=20Revert=20mistak?= =?UTF-8?q?en=20check=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031?= =?UTF-8?q?510:=20THOR-326=20Changes=20to=20DAO=20layer=20to=20allow=20upd?= =?UTF-8?q?ate=20of=20account=20type.=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031513:=20Cloud=20Console=20updates=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031514:=20Switch=20Networ?= =?UTF-8?q?k=20now=20uses=20tenants=20from=20the=20user=20object=20(instea?= =?UTF-8?q?d=20of=20making=20a=20remote=20call)=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031515:=20THOR-326=20Changes=20to=20the?= =?UTF-8?q?=20REST=20&=20Service=20layer=20to=20allow=20update=20of=20acco?= =?UTF-8?q?unt=20type.=20=20=20=2048075:=20Merged=20BRANCHES/DEV/CONV=5FV4?= =?UTF-8?q?13=20to=20BRANCHES/DEV/CONV=5FHEAD:=20(effectively=20record-onl?= =?UTF-8?q?y=20-=20no=20changes)=20=20=20=20=20=20=20=20=2046849:=20Merged?= =?UTF-8?q?=20PATCHES/V4.1.3=20to=20DEV/CONV=5FV413=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2046779:=20ALF-17967:=20Error=20in=20org.alf?= =?UTF-8?q?resco.repo.workflow.WorkflowServiceImpl.getPooledTasks=20on=20S?= =?UTF-8?q?tartUp.=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20Improved?= =?UTF-8?q?=20fix=20that=20uses=20the=20bridge=20table=20cache=20if=20it?= =?UTF-8?q?=20is=20available=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20?= =?UTF-8?q?Groups=20queried=20for=20pooled=20tasks=20still=20limited=20to?= =?UTF-8?q?=20100=20by=20default=20but=20can=20be=20configured=20with=20sy?= =?UTF-8?q?stem.workflow.maxAuthoritiesForPooledTasks=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20-=20Overall=20number=20of=20results=20can?= =?UTF-8?q?=20be=20cut=20off=20with=20system.workflow.maxPooledTasks=20=20?= =?UTF-8?q?=20=2048076:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRANCHE?= =?UTF-8?q?S/DEV/CONV=5FHEAD:=20=20=20=20=20=20=20=20=2046855:=20Merged=20?= =?UTF-8?q?BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2035706:=20Merged=20BRANCHES/DEV?= =?UTF-8?q?/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031049:=20THOR-175:=20set=20and=20?= =?UTF-8?q?enforce=20per-tenant=20quota=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031053:=20THOR-204:=20Add=20dev=20email?= =?UTF-8?q?=20mode=20option=20=20=20=2048077:=20Merged=20DEV/CONV=5FV413?= =?UTF-8?q?=20to=20DEV/CONV=5FHEAD=20(RECORD=20ONLY)=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2046944:=20Merge=20fallout=20-=20fix=20compile=20error.=20?= =?UTF-8?q?=20=20=2048078:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRAN?= =?UTF-8?q?CHES/DEV/CONV=5FHEAD:=20(already=20fixed=20-=20no=20changes)=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2046858:=20Fix=20compile=20error=20=20=20?= =?UTF-8?q?=2048079:=20Merged=20DEV/CONV=5FV413=20to=20DEV/CONV=5FHEAD=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2046953:=20Merged=20from=20BRANCHES/DEV/C?= =?UTF-8?q?LOUD2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2035767:=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHE?= =?UTF-8?q?S/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031516?= =?UTF-8?q?:=20Hidden=20admin=20-=20attempt=202=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031517:=20THOR-326.=20Update=20to=20REST-cient?= =?UTF-8?q?=20rcq=20file=20following=2031515,=20which=20allows=20for=20acc?= =?UTF-8?q?ount=20upgrade.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20315?= =?UTF-8?q?18:=20THOR-326.=20DaveC=20asked=20me=20to=20move=20the=20paid?= =?UTF-8?q?=20business=20account=20type=20out=20of=20test=20config=20and?= =?UTF-8?q?=20into=20product=20config.=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031519:=20After=20tenant=20switch=20the=20client=20side?= =?UTF-8?q?=20resources=20are=20more=20sensitive=20(new=20requires=20/res)?= =?UTF-8?q?=20which=20it=20didn't=20before.=20This=20solves=20the=20webpre?= =?UTF-8?q?view=20bug=20and=20some=20other=20minor=20stuff.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031520:=20THOR-175:=20Set=20and=20?= =?UTF-8?q?enforce=20file=20space=20quota=20for=20tenant=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031522:=20THOR-330.=20Return=20Accoun?= =?UTF-8?q?t=20Class=20data=20in=20Account=20REST=20API.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031523:=20THOR-330.=20Added=20new=20r?= =?UTF-8?q?sp=20data=20into=20desc.xml=20sample=20response.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031524:=20THOR-322:=20refactor=20t?= =?UTF-8?q?enant=20file=20usage/quota=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031525:=20Skip=20activity=20post=20lookups=20that=20hav?= =?UTF-8?q?e=20exceptions=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203152?= =?UTF-8?q?6:=20Root=20webdav=20to=20st:sites=20for=20now=20(as=20per=20cu?= =?UTF-8?q?rrent=20beta.alfresco.com)=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031528:=20THOR-323=20&=20THOR-324=20Promotion=20and=20d?= =?UTF-8?q?emotion=20of=20users=20to/from=20NetworkAdmin.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031534:=20Account=20Summary=20now?= =?UTF-8?q?=20also=20displays=20name=20&=20summary=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031535:=20Fix=20for=20THOR-320.=20Alfresco?= =?UTF-8?q?=20logo=20image=20in=20the=20various=20Cloud=20emails=20is=20br?= =?UTF-8?q?oken.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031538:=20Acco?= =?UTF-8?q?unt=20Summary=20now=20handles=20-2=20&=20MultiSelectAutoComplet?= =?UTF-8?q?e=20=20doesn't=20bounce=20when=20selecting=20first=20item=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031540:=20Implementation?= =?UTF-8?q?=20of=20THOR-335=20webscript=20for=20account-types.get=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031541:=20Account=20Summary?= =?UTF-8?q?=20now=20displays=20date=20correctly=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031542:=20Some=20paths=20to=20client=20side=20?= =?UTF-8?q?resources=20that=20were=20missing=20"/res"=20in=20the=20path=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031547:=20Various=20la?= =?UTF-8?q?bel=20changes=20according=20to=20Kathryns=20docs=20&=20some=20n?= =?UTF-8?q?ew=20login/forgot=20password=20links=20in=20invite/signup=20for?= =?UTF-8?q?ms=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031555:=20Refacto?= =?UTF-8?q?red=20Analytics=20Service=20to=20send=20JSON=20Analytics=20prop?= =?UTF-8?q?erties=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031557:=20Som?= =?UTF-8?q?e=20changes=20to=20cloud=20email=20templates=20following=20feed?= =?UTF-8?q?back=20from=20Kathryn,=20Erik.=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031558:=20THOR-322:=20refactor=20tenant=20file=20usa?= =?UTF-8?q?ge/quota=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031559:=20T?= =?UTF-8?q?urned=20'sign=20up'=20email=20URLs=20into=20links=20rather=20th?= =?UTF-8?q?an=20text.=20Yes,=20we'll=20make=20these=20buttons=20at=20some?= =?UTF-8?q?=20point=20but=20I=20just=20want=20them=20to=20be=20clickable?= =?UTF-8?q?=20for=20now.=20=20=20=2048080:=20Merged=20DEV/CONV=5FV413=20to?= =?UTF-8?q?=20DEV/CONV=5FHEAD=20(UI=20ONLY)=20=20=20=20=20=20=20=20=204695?= =?UTF-8?q?4:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CO?= =?UTF-8?q?NV=5FV413=20=20=20=20=20=20=20=20=20=20=20=2035771:=20Merged=20?= =?UTF-8?q?BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031563:=20THOR-123:=20modules=20ar?= =?UTF-8?q?e=20no=20longer=20started=20for=20each=20tenant=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031566:=20Update=20account=20class?= =?UTF-8?q?=20display=20names:=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031567:=20THOR-123:=20temp'=20put=20back=20"applyToTenants=3D?= =?UTF-8?q?true"=20...=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031575:?= =?UTF-8?q?=20THOR-123:=20pre-req=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2031579:=20Fix=20issue=20where=20setting=20a=20preference?= =?UTF-8?q?=20meant=20that=20the=20person=20could=20no=20longer=20login:?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031581:=20Addition?= =?UTF-8?q?=20of=20isNetworkAdmin,=20accountClassName=20and=20accountClass?= =?UTF-8?q?DisplayName=20to=20the=20metadata.get=20webscript,=20as=20requi?= =?UTF-8?q?red=20by=20Erik.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031?= =?UTF-8?q?582:=20Re-enabling=20RenditionServiceIntegrationTest=20which=20?= =?UTF-8?q?was=20failing.=20See=20THOR-106.=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031584:=20THOR-123:=20pre-req=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031585:=20THOR-347:=20disable=20test?= =?UTF-8?q?=20(pending=20this=20JIRA)=20-=20ChainingUserRegistrySynchroniz?= =?UTF-8?q?erTest=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031590:=20=20?= =?UTF-8?q?Account=20changes=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203?= =?UTF-8?q?1591:=20Upgrade=20accound=20button=20is=20now=20a=20mailto=20li?= =?UTF-8?q?nk=20pointing=20to=20sales@alfresco.com=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031592:=20Added=20isNetworkAdmin=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031593:=20Made=20sure=20tool?= =?UTF-8?q?tips=20are=20hidden=20when=20a=20dialog/overly=20is=20showed/hi?= =?UTF-8?q?dden=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031594:=20Disab?= =?UTF-8?q?ling=20test=20again=20pending=20proper=20fix.=20THOR-106=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031595:=20Reduce=20log=20?= =?UTF-8?q?level=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031600:=20THOR?= =?UTF-8?q?-123:=20mark=20modules=20with=20"applyToTenants=3Dfalse"=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031601:=20Tooltips=20now?= =?UTF-8?q?=20dissapear=20when=20panel/overlays=20are=20destroyed=20(not?= =?UTF-8?q?=20only=20hidden)=20=20=20=2048081:=20Merged=20DEV/CONV=5FV413?= =?UTF-8?q?=20to=20DEV/CONV=5FHEAD=20=20=20=20=20=20=20=20=2046955:=20Merg?= =?UTF-8?q?ed=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2035779:=20Merged=20BRANCHES/?= =?UTF-8?q?DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031605:=20(RECORD=20ONLY)=20THOR-336.=20Fix?= =?UTF-8?q?ing=20/res/themes=20URLs=20in=20activity=20emails.=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20Fix=20/re?= =?UTF-8?q?s/themes=20URL=20in=20newly=20located=20activity=20emails.=20?= =?UTF-8?q?=20=20=2048088:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRAN?= =?UTF-8?q?CHES/DEV/CONV=5FHEAD:=20=20=20=20=20=20=20=20=2046874:=20Merged?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2035709:=20Merged=20BRANCHES/?= =?UTF-8?q?DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031054:=20Fix=20for=20email=20t?= =?UTF-8?q?emplates=20(getDirectReadableChannel=20->=20File=20does=20not?= =?UTF-8?q?=20exist)=20=20=20=20=20=20=20=20=2046875:=20Merged=20BRANCHES/?= =?UTF-8?q?DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2035711:=20Merged=20BRANCHES/DEV/THOR1=20?= =?UTF-8?q?to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031055:=20Re-enable=20activity=20feed=20not?= =?UTF-8?q?ifications=20and=20subscriptions=20(followers)=20=20=20=2048094?= =?UTF-8?q?:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRANCHES/DEV/CONV?= =?UTF-8?q?=5FHEAD:=20=20=20=20=20=20=20=20=2046894:=20Merged=20BRANCHES/D?= =?UTF-8?q?EV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2035759:=20Merged=20BRANCHES/DEV/THOR1=20to?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031386:=20Added=20some=20tenancy-awareness=20t?= =?UTF-8?q?o=20ActionService.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031388:=20Addition=20of=20accountTypeId=20to=20metad?= =?UTF-8?q?ata.get=20webscript.=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031391:=20Fix=20for=20unreported=20issue=20that?= =?UTF-8?q?=20arises=20from=20the=20invitation=20workflow=20having=20moved?= =?UTF-8?q?=20from=20the=20system=20to=20the=20inviter=20tenant.=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031392:=20Build?= =?UTF-8?q?=20fixes:=20Add=20pseudo-support=20for=20tenant=20switching=20i?= =?UTF-8?q?n=20web=20script=20test=20f/w=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031393:=20The=20final=20fix=20for=20the?= =?UTF-8?q?=20'external=20user=20invites=20other=20external=20user'=20scen?= =?UTF-8?q?ario.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203?= =?UTF-8?q?1398:=20Tenant=20usage/quota=20-=20site=20count=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031405:=20Build=20fix?= =?UTF-8?q?=20for=20failing=20ActionService=20tests.=20Compensating=20acti?= =?UTF-8?q?ons=20were=20not=20running=20on=20the=20correct=20tenant.=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031407:=20Res?= =?UTF-8?q?olve=20THOR-248:=20Extensions=20is=20not=20deployed=20as=20part?= =?UTF-8?q?=20of=20the=20build=20=20=20=2048095:=20Merged=20BRANCHES/DEV/C?= =?UTF-8?q?ONV=5FV413=20to=20BRANCHES/DEV/CONV=5FHEAD:=20(repo=20pre-merge?= =?UTF-8?q?)=20=20=20=20=20=20=20=20=2046911:=20Merged=20from=20BRANCHES/D?= =?UTF-8?q?EV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2035757:=20Merged=20BRANCHES/DEV/THOR1=20to?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031367:=20Merged=20BRANCHES/DEV/THOR1SURF=20to=20BRA?= =?UTF-8?q?NCHES/DEV/THOR1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2030971:=20(RECORD=20ONLY)=20Creating=20SURF?= =?UTF-8?q?=20update=20branch=20for=20THOR1=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2030979:=20Commit=20initial?= =?UTF-8?q?=20Surf=20lib=20changes,=20Cloud=20classes=20and=20config=20ove?= =?UTF-8?q?rrides=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2030980:=20Add=20custom=20Cloud=20Surf=20authenticator,?= =?UTF-8?q?=20connector,=20remote=20store,=20user=20and=20user=20factory?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031015:=20Renamed=20classes=20and=20references=20from=20Cloud?= =?UTF-8?q?=20to=20Tenant,=20custom=20page=20view,=20page=20view=20resolve?= =?UTF-8?q?rs,=20URLModel=20+=20factory,=20URLHelper=20+=20factory=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20310?= =?UTF-8?q?76:=20Successful=20signup=20and=20page=20redirection=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031091:?= =?UTF-8?q?=20Correct=20redirects=20from=20=20and?= =?UTF-8?q?=20/=20URLs=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031098:=20?= =?UTF-8?q?Updated=20Surf=20libs=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031132:=20Tenant=20specific=20implementa?= =?UTF-8?q?tion=20of=20PathStoreObjectPersister=20-=20Surf=20modelobject?= =?UTF-8?q?=20cache=20is=20now=20partitioned=20by=20the=20tenant=20name.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031133:=20Updated=20Surf=20libs=20and=20JavaDoc=20updates=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?31155:=20Initial=20code=20to=20handle=20attempted=20access=20to?= =?UTF-8?q?=20unauthorised=20tenants,=20secondary=20tenants=20added=20to?= =?UTF-8?q?=20TenantUser=20and=20page/activations=20filter=20rule=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031210:?= =?UTF-8?q?=20Fixed=20401=20&=20409=20errors=20on=20remote=20GET/POST=20ca?= =?UTF-8?q?lls.=20Logout=20redirection=20support.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031229:=20Signup=20and?= =?UTF-8?q?=20invitiation=20completion=20updates=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031242:=20Fixed=20up?= =?UTF-8?q?=20invitation,=20signup=20and=20tenant=20switching=20problems?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031270:=20Fixed=20forms=20issue=20(can=20now=20create=20folde?= =?UTF-8?q?rs=20in=20doc=20lib)=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031277:=20THOR-207.=20Invitation=20workf?= =?UTF-8?q?lows=20now=20run=20in=20the=20inviter's=20tenant=20rather=20tha?= =?UTF-8?q?n=20the=20default=20tenant.=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20This=20is=20checked?= =?UTF-8?q?=20in=20on=20a=20side-branch=20because=20the=20invitation=20ema?= =?UTF-8?q?il's=20accept/reject=20links=20include=20the=20tenantId=20and?= =?UTF-8?q?=20this=20tenant-aware=20Share=20URL=20is=20not=20yet=20support?= =?UTF-8?q?ed=20on=20the=20THOR1=20branch.=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20I=20removed=20v?= =?UTF-8?q?arious=20TenantUtil.runAsWork=20calls=20which=20were=20causing?= =?UTF-8?q?=20the=20workflow=20to=20run=20on=20the=20default=20tenant=20ra?= =?UTF-8?q?ther=20then=20the=20current=20tenant.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20SendCloud?= =?UTF-8?q?InvitationEmailDelegate.createInvitationUrl=20now=20includes=20?= =?UTF-8?q?the=20tenantId=20in=20the=20Share=20URL=20it=20generates.=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20Added=20new=20test=20cases=20at=20the=20Java=20API=20l?= =?UTF-8?q?evel.=20(Was=20formerly=20just=20at=20REST=20API=20level).=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?31286:=20(RECORD=20ONLY)=20Reset=20solrcore.properties=20files?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031297:=20Fixed=20FlashUpload=20problem=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031298:=20Fixed=20a?= =?UTF-8?q?pplication=20context=20only=20login=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031302:=20Fixed=20no=20us?= =?UTF-8?q?er=20profile=20image=20url=20issue=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031306:=20Updates=20to=20?= =?UTF-8?q?TenantUserFactory=20to=20defensively=20handle=20missing=20tenan?= =?UTF-8?q?t=20data=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031326:=20Repo=20switch=20tenant=20fixes:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031356:?= =?UTF-8?q?=20Resolve=20switch=20tenant=20niggles,=20with=20assistance=20f?= =?UTF-8?q?rom=20Erik:=20=20=20=2048109:=20Merged=20BRANCHES/DEV/CONV=5FV4?= =?UTF-8?q?13=20to=20BRANCHES/DEV/CONV=5FHEAD:=20=20=20=20=20=20=20=20=204?= =?UTF-8?q?6917:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV?= =?UTF-8?q?=5FV413:=20-=20pre-merge=20of=20repo=20parts=20(not=20mergeinfo?= =?UTF-8?q?/slingshot/web-framework-commons)=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2035766:=20Merged=20BRANCHES/DEV/THOR1=20to=20BRANCHE?= =?UTF-8?q?S/DEV/CLOUD1:=20=20=20=20=20=20=20=20=2046918:=20Merged=20BRANC?= =?UTF-8?q?HES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20-=20pre-merge?= =?UTF-8?q?=20of=20repo=20parts=20(not=20mergeinfo/slingshot)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2035767:=20Merged=20BRANCHES/DEV/TH?= =?UTF-8?q?OR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=2046919?= =?UTF-8?q?:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV41?= =?UTF-8?q?3:=20=20=20=20=20=20=20=20=20=20=20=20=20=2035768:=20Fix=20comp?= =?UTF-8?q?ile=20issue=20from=20merge=20=20=20=20=20=20=20=20=2046921:=20M?= =?UTF-8?q?erge=20fallout=20-=20fix=20compile=20error.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2046949:=20Test=20fallout=20=20=20=20=20=20=20=20=204712?= =?UTF-8?q?6:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV4?= =?UTF-8?q?13:=20-=20pre-merge=20of=20repo=20parts=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2035954:=20Merged=20BRANCHES/DEV/THOR1=5FSPRINT?= =?UTF-8?q?S=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2035960:=20Merged=20BRANCHES/DEV/THOR1=5FSPRINTS=20to=20?= =?UTF-8?q?BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=203?= =?UTF-8?q?5961:=20Merged=20BRANCHES/DEV/THOR1=5FSPRINTS=20to=20BRANCHES/D?= =?UTF-8?q?EV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=2035962:=20Me?= =?UTF-8?q?rged=20BRANCHES/DEV/THOR1=5FSPRINTS=20to=20BRANCHES/DEV/CLOUD1:?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2035963:=20Merged=20BRA?= =?UTF-8?q?NCHES/DEV/THOR1=5FSPRINTS=20to=20BRANCHES/DEV/CLOUD1:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2035964:=20Spring=20Surf=20libra?= =?UTF-8?q?ry=20refresh=20=20=20=20=20=20=20=20=20=20=20=20=20=2035995:=20?= =?UTF-8?q?Fix=20merge=20issue=20=20=20=20=20=20=20=20=20=20=20=20=20=2035?= =?UTF-8?q?999:=20Fix=20merge=20issue=20=20=20=20=20=20=20=20=2047144:=20F?= =?UTF-8?q?ix=20merge/test=20failures=20(WCMTestSuite)=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2047539:=20CLOUD-1375=20-=20fix=20WCM=20unit=20test=20fa?= =?UTF-8?q?llout:=20SandboxServiceImplTest.testDeleteSandbox=20+=20WebProj?= =?UTF-8?q?ectServiceImplTest.testDeleteWebProject=20=20=20=2048111:=20Mer?= =?UTF-8?q?ged=20BRANCHES/DEV/CONV=5FV413=20to=20BRANCHES/DEV/CONV=5FHEAD:?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2046954:=20Merged=20from=20BRANCHES/DE?= =?UTF-8?q?V/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2035771:=20Merged=20BRANCHES/DEV/THOR1=20to?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031563:=20THOR-123:=20modules=20are=20no=20longer=20?= =?UTF-8?q?started=20for=20each=20tenant=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031566:=20Update=20account=20class=20display?= =?UTF-8?q?=20names:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20315?= =?UTF-8?q?67:=20THOR-123:=20temp'=20put=20back=20"applyToTenants=3Dtrue"?= =?UTF-8?q?=20...=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031575:?= =?UTF-8?q?=20THOR-123:=20pre-req=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031579:=20Fix=20issue=20where=20setting=20a=20prefer?= =?UTF-8?q?ence=20meant=20that=20the=20person=20could=20no=20longer=20logi?= =?UTF-8?q?n:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031581:=20A?= =?UTF-8?q?ddition=20of=20isNetworkAdmin,=20accountClassName=20and=20accou?= =?UTF-8?q?ntClassDisplayName=20to=20the=20metadata.get=20webscript,=20as?= =?UTF-8?q?=20required=20by=20Erik.=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031582:=20Re-enabling=20RenditionServiceIntegrationT?= =?UTF-8?q?est=20which=20was=20failing.=20See=20THOR-106.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031584:=20THOR-123:=20pre-re?= =?UTF-8?q?q=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031585:=20TH?= =?UTF-8?q?OR-347:=20disable=20test=20(pending=20this=20JIRA)=20-=20Chaini?= =?UTF-8?q?ngUserRegistrySynchronizerTest=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031590:=20=20Account=20changes=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2031591:=20Upgrade=20accound?= =?UTF-8?q?=20button=20is=20now=20a=20mailto=20link=20pointing=20to=20sale?= =?UTF-8?q?s@alfresco.com=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031592:=20Added=20isNetworkAdmin=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031593:=20Made=20sure=20tooltips=20are?= =?UTF-8?q?=20hidden=20when=20a=20dialog/overly=20is=20showed/hidden=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031594:=20Disabling?= =?UTF-8?q?=20test=20again=20pending=20proper=20fix.=20THOR-106=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031595:=20Reduce=20log?= =?UTF-8?q?=20level=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203160?= =?UTF-8?q?0:=20THOR-123:=20mark=20modules=20with=20"applyToTenants=3Dfals?= =?UTF-8?q?e"=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031601:=20T?= =?UTF-8?q?ooltips=20now=20dissapear=20when=20panel/overlays=20are=20destr?= =?UTF-8?q?oyed=20(not=20only=20hidden)=20=20=20=20=20=20=20=20=2046956:?= =?UTF-8?q?=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV?= =?UTF-8?q?=5FV413=20=20=20=20=20=20=20=20=20=20=20=20=20=2035782:=20Merge?= =?UTF-8?q?d=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031607:=20=20"Hide=20ever?= =?UTF-8?q?ything=20but=20the=20doclib"=20customizations=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20Dashlets=20?= =?UTF-8?q?adjustments=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20*=20Addons=20RSS=20feed=20-=20hidden=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20*=20Site=20Calendar=20-=20hidden=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20*=20Content=20I'm?= =?UTF-8?q?=20editing=20-=20added=20<@markup>=20extension=20points=20so=20?= =?UTF-8?q?blog,=20wiki=20&=20forum=20sections=20are=20hidden=20by=20cloud?= =?UTF-8?q?=20extension=20module=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20*=20Site=20Data=20List=20-=20hidd?= =?UTF-8?q?en=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20*=20Site=20Links=20-=20hidden=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20*=20Wiki=20-?= =?UTF-8?q?=20hidden=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20*=20User=20Calendar=20-=20hidden=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20URL?= =?UTF-8?q?=20rewrites=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20*=20Forgot=20&=20reset=20password=20urls?= =?UTF-8?q?=20now=20prettyfied,=20not=20using=20"-default-/"=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20Dupli?= =?UTF-8?q?cated=20slingshot=20presets=20to=20avoid=20future=20slingshot?= =?UTF-8?q?=20changes=20popping=20up=20in=20the=20cloud=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031611:=20MT:=20fix=20ability?= =?UTF-8?q?=20to=20delete=20a=20disabled=20tenant=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031612:=20THOR-339:=20Disable/enable?= =?UTF-8?q?=20logins=20for=20a=20network=20(account=20update)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031621:=20THOR-106.=20Tak?= =?UTF-8?q?ing=20a=20failing=20test=20class=20out=20again,=20pending=20fix?= =?UTF-8?q?.=20Hmmmm.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031?= =?UTF-8?q?623:=20THOR-357=20-=20support=20shared=20CMIS=20dictionary=20?= =?UTF-8?q?=20=20=2048112:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRAN?= =?UTF-8?q?CHES/DEV/CONV=5FHEAD:=20(no=20changes)=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2046957:=20Test=20fallout=20=20=20=2048113:=20Merged=20BRANCHE?= =?UTF-8?q?S/DEV/CONV=5FV413=20to=20BRANCHES/DEV/CONV=5FHEAD:=20(no=20chan?= =?UTF-8?q?ges=20-=20already=20pre-merged)=20=20=20=20=20=20=20=20=20=2046?= =?UTF-8?q?911:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/?= =?UTF-8?q?CONV=5FV413=20=20=20=20=20=20=20=20=20=20=20=20=20=2035757:=20M?= =?UTF-8?q?erged=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031367:=20Merged=20BRA?= =?UTF-8?q?NCHES/DEV/THOR1SURF=20to=20BRANCHES/DEV/THOR1:=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2030971:=20(RE?= =?UTF-8?q?CORD=20ONLY)=20Creating=20SURF=20update=20branch=20for=20THOR1?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2030979:=20Commit=20initial=20Surf=20lib=20changes,=20Cloud=20?= =?UTF-8?q?classes=20and=20config=20overrides=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2030980:=20Add=20custom=20?= =?UTF-8?q?Cloud=20Surf=20authenticator,=20connector,=20remote=20store,=20?= =?UTF-8?q?user=20and=20user=20factory=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031015:=20Renamed=20classes=20and?= =?UTF-8?q?=20references=20from=20Cloud=20to=20Tenant,=20custom=20page=20v?= =?UTF-8?q?iew,=20page=20view=20resolvers,=20URLModel=20+=20factory,=20URL?= =?UTF-8?q?Helper=20+=20factory=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031076:=20Successful=20signup=20and=20pa?= =?UTF-8?q?ge=20redirection=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031091:=20Correct=20redirects=20from=20=20and=20/=20URLs=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031098:=20Updated=20Surf=20libs=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031132:=20Tenant?= =?UTF-8?q?=20specific=20implementation=20of=20PathStoreObjectPersister=20?= =?UTF-8?q?-=20Surf=20modelobject=20cache=20is=20now=20partitioned=20by=20?= =?UTF-8?q?the=20tenant=20name.=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031133:=20Updated=20Surf=20libs=20and=20?= =?UTF-8?q?JavaDoc=20updates=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031155:=20Initial=20code=20to=20handle=20at?= =?UTF-8?q?tempted=20access=20to=20unauthorised=20tenants,=20secondary=20t?= =?UTF-8?q?enants=20added=20to=20TenantUser=20and=20page/activations=20fil?= =?UTF-8?q?ter=20rule=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031210:=20Fixed=20401=20&=20409=20errors=20on=20r?= =?UTF-8?q?emote=20GET/POST=20calls.=20Logout=20redirection=20support.=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?31229:=20Signup=20and=20invitiation=20completion=20updates=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?31242:=20Fixed=20up=20invitation,=20signup=20and=20tenant=20swi?= =?UTF-8?q?tching=20problems=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031270:=20Fixed=20forms=20issue=20(can=20no?= =?UTF-8?q?w=20create=20folders=20in=20doc=20lib)=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2031277:=20THOR-207.=20?= =?UTF-8?q?Invitation=20workflows=20now=20run=20in=20the=20inviter's=20ten?= =?UTF-8?q?ant=20rather=20than=20the=20default=20tenant.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20Thi?= =?UTF-8?q?s=20is=20checked=20in=20on=20a=20side-branch=20because=20the=20?= =?UTF-8?q?invitation=20email's=20accept/reject=20links=20include=20the=20?= =?UTF-8?q?tenantId=20and=20this=20tenant-aware=20Share=20URL=20is=20not?= =?UTF-8?q?=20yet=20supported=20on=20the=20THOR1=20branch.=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?I=20removed=20various=20TenantUtil.runAsWork=20calls=20which=20?= =?UTF-8?q?were=20causing=20the=20workflow=20to=20run=20on=20the=20default?= =?UTF-8?q?=20tenant=20rather=20then=20the=20current=20tenant.=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20SendCloudInvitationEmailDelegate.createInvitationUrl=20now?= =?UTF-8?q?=20includes=20the=20tenantId=20in=20the=20Share=20URL=20it=20ge?= =?UTF-8?q?nerates.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20Added=20new=20test=20cases=20at=20the?= =?UTF-8?q?=20Java=20API=20level.=20(Was=20formerly=20just=20at=20REST=20A?= =?UTF-8?q?PI=20level).=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031286:=20(RECORD=20ONLY)=20Reset=20solrcore.prop?= =?UTF-8?q?erties=20files=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031297:=20Fixed=20FlashUpload=20problem=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20312?= =?UTF-8?q?98:=20Fixed=20application=20context=20only=20login=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031302:=20?= =?UTF-8?q?Fixed=20no=20user=20profile=20image=20url=20issue=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031306:=20?= =?UTF-8?q?Updates=20to=20TenantUserFactory=20to=20defensively=20handle=20?= =?UTF-8?q?missing=20tenant=20data=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031326:=20Repo=20switch=20tenant=20fi?= =?UTF-8?q?xes:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031356:=20Resolve=20switch=20tenant=20niggles,=20with?= =?UTF-8?q?=20assistance=20from=20Erik:=20=20=20=2048114:=20Merged=20BRANC?= =?UTF-8?q?HES/DEV/CONV=5FV413=20to=20BRANCHES/DEV/CONV=5FHEAD:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2046962:=20Merged=20BRANCHES/DEV/CLOUD2=20to?= =?UTF-8?q?=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=2040147:=20(RECORD=20ONLY)=20French:=20Cloud=20Translation?= =?UTF-8?q?=20update=20from=20Gloria=20(based=20on=20EN=20rev38372)=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2042709:=20(RECORD=20ONLY)=20FRE?= =?UTF-8?q?NCH:=20Translation=20updates=20based=20on=20EN=20r42416=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2042871:=20(RECORD=20ONLY)=20GER?= =?UTF-8?q?MAN:=20Cloud=20Translation,=20based=20on=20r=2042416=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2042879:=20(RECORD=20ONLY)=20SPANIS?= =?UTF-8?q?H:=20Cloud=20Translation,=20based=20on=20r=2042416=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2042890:=20(RECORD=20ONLY)=20ITALIAN:?= =?UTF-8?q?=20Cloud=20Translation,=20based=20on=20r=2042416=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2043879:=20(RECORD=20ONLY)=20FRENCH:=20Tr?= =?UTF-8?q?anslation=20updates=20based=20on=20EN=20r43703=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2043983:=20(RECORD=20ONLY)=20GERMAN:=20Tr?= =?UTF-8?q?anslation=20updates=20based=20on=20EN=20r43703=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2043984:=20(RECORD=20ONLY)=20SPANISH:=20T?= =?UTF-8?q?ranslation=20updates=20based=20on=20EN=20r43703=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2043985:=20(RECORD=20ONLY)=20FRENCH:=20Tr?= =?UTF-8?q?anslation=20updates=20based=20on=20EN=20r43703,=20includes=20fi?= =?UTF-8?q?le=20missing=20from=20previous=20commit.=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2043986:=20(RECORD=20ONLY)=20ITALIAN:=20Transla?= =?UTF-8?q?tion=20updates=20based=20on=20EN=20r43703.=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2043987:=20(RECORD=20ONLY)=20JAPANESE:=20Transl?= =?UTF-8?q?ation=20updates=20based=20on=20EN=20r43703.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2044031:=20(RECORD=20ONLY)=20JAPANESE:=20Tra?= =?UTF-8?q?nslation=20updates=20based=20on=20EN=20r43703.=20Corrects=20fil?= =?UTF-8?q?e=20missed=20from=20previous=20commit.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2044032:=20(RECORD=20ONLY)=20GERMAN:=20Translation?= =?UTF-8?q?=20updates=20based=20on=20EN=20r43703.=20Corrects=20missing=20l?= =?UTF-8?q?ine=20break.=20=20=20=20=20=20=20=20=20=20=20=20=2045329:=20(RE?= =?UTF-8?q?CORD=20ONLY)=20FRENCH:=20Cloud=20translation=20updates=20based?= =?UTF-8?q?=20on=20EN=20r45266=20=20=20=20=20=20=20=20=20=20=20=20=2045330?= =?UTF-8?q?:=20(RECORD=20ONLY)=20GERMAN:=20Cloud=20translation=20updates?= =?UTF-8?q?=20based=20on=20EN=20r45266=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2045332:=20(RECORD=20ONLY)=20SPANISH:=20Cloud=20translation=20?= =?UTF-8?q?updates=20based=20on=20EN=20r45266=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2045333:=20(RECORD=20ONLY)=20JAPANESE:=20Cloud=20tran?= =?UTF-8?q?slation=20updates=20based=20on=20EN=20r45266=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2045427:=20(RECORD=20ONLY)=20SPANISH:=20Clou?= =?UTF-8?q?d=201=20translation=20updates=20based=20on=20EN=20r45266=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2045718:=20(RECORD=20ONLY)=20ITA?= =?UTF-8?q?LIAN:=20Translation=20updates=20based=20on=20EN=20r45266=20(mis?= =?UTF-8?q?sed=20from=20previous=20bundle=20import)=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2045838:=20(RECORD=20ONLY)=20FRENCH:=20Cloud=20?= =?UTF-8?q?Translation=20update=20based=20on=20EN=20r45266=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2045966:=20(RECORD=20ONLY)=20Translation?= =?UTF-8?q?=20update=20to=20fix=20CLOUD-1270=20in=20FR=20and=20ES=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2046365:=20(RECORD=20ONLY)=20ALL=20?= =?UTF-8?q?LANG:=20Translation=20updates=20based=20on=20EN=20r46289=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2046366:=20(RECORD=20ONLY)=20ALL?= =?UTF-8?q?=20LANG:=20Updates=20copyright=20year=20to=202013=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2046377:=20(RECORD=20ONLY)=20ALL=20LAN?= =?UTF-8?q?G:=20Adds=20strings=20missing=20from=20previous=20commit.=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2047192:=20Merged=20BRANCHES/DEV/CLOUD2?= =?UTF-8?q?=20to=20BRANCHES/DEV/CONV=5FV413:=20(record-only=20-=20WebDAV?= =?UTF-8?q?=20mostly=20resolved=20as=20part=20of=2036117=20merge)=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2036408:=20(RECORD=20ONLY)=20Merged?= =?UTF-8?q?=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2036404:=20Merged=20BRA?= =?UTF-8?q?NCHES/DEV/THOR1=5FSPRINTS=20to=20BRANCHES/DEV/THOR1:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20360?= =?UTF-8?q?60:=20THOR-1373:=20Proxied=20WebDAV=20must=20generate=20correct?= =?UTF-8?q?=20URLs=20when=20URL-rewriting=20is=20used.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2036083:=20THO?= =?UTF-8?q?R-1373:=20Proxied=20WebDAV=20must=20generate=20correct=20URLs?= =?UTF-8?q?=20when=20URL-rewriting=20is=20used.=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2047369:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CO?= =?UTF-8?q?NV=5FV413:=20=20=20=20=20=20=20=20=20=20=20=20=2041180:=20(RECO?= =?UTF-8?q?RD=20ONLY)=20Merged=20BRANCHES/DEV/FEATURES/CLOUD1=5FCLOUDSYNC?= =?UTF-8?q?=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2040482:=20ALF-13998:=20'No=20items'=20error?= =?UTF-8?q?=20is=20highlighted=20in=20red,=20even=20that=20is=20not=20seve?= =?UTF-8?q?r=20error.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20-=20ALF-15453:=20Incorrect=20manage=20permissions?= =?UTF-8?q?=20working=20for=20a=20file/folder=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20Merged=20BRANCHES/DEV/FEATURES/CLOUD?= =?UTF-8?q?1=5FCLOUDSYNC=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2040486:=20ALF-15453:?= =?UTF-8?q?=20Incorrect=20manage=20permissions=20working=20for=20a=20file/?= =?UTF-8?q?folder=20=20=20=20=20=20=20=20=20=2047377:=20Merged=20BRANCHES/?= =?UTF-8?q?DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2041048:=20(RECORD=20ONLY)=20Merged=20DEV/V4?= =?UTF-8?q?.1-BUG-FIX=20to=20DEV/CLOUD1=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2040382:=20Fix=20for=20ALF-15491=20SOLR?= =?UTF-8?q?=20is=20generating=20queries=20for=20lucene=20style=20cross-lan?= =?UTF-8?q?guage=20support=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2040632:=20Fix=20for=20ALF-15487=20Search=20not=20workin?= =?UTF-8?q?g=20for=20queries=20containing=203-digit=20versions=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20Fix=20for=20ALF-15356=20SOLR=20doesn't=20support=20searching?= =?UTF-8?q?=20by=20cm:name=20of=20file=20with=20underscore=20and=20dots=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2040662:=20?= =?UTF-8?q?Eclipse=20classpath=20fixes=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2041032:=20Fix=20for=20ALF-15753=20=20=20=20?= =?UTF-8?q?=20=20=20Infinite=20loop=20during=20Solr=20ACL=20indexing=20whe?= =?UTF-8?q?n=20ACL=20Changeset=20batch=20is=20empty=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2047393:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/?= =?UTF-8?q?DEV/CONV=5FV413:=20=20=20=20=20=20=20=20=20=20=20=20=20=20Merge?= =?UTF-8?q?d=20DEV/CLOUD1-BUG-FIX=20into=20DEV/CLOUD1:=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2041674:=20ALF-15967:=20Using?= =?UTF-8?q?=20START=5FUSER=5FID=5F=20instead=20of=20"initiator"=20property?= =?UTF-8?q?=20to=20query=20process=20instances=20started=20by=20user=20X?= =?UTF-8?q?=20to=20prevent=20extra=20joins=20+=20removed=20unused=20consta?= =?UTF-8?q?nts=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2041650:?= =?UTF-8?q?=20Fixed=20CLOUD-667:=20Merged=20fix=20for=20ALF-14438=20into?= =?UTF-8?q?=20CLOUD1-BUG-FIX=20+=20using=20START=5FUSER=5FID=5F=20instead?= =?UTF-8?q?=20of=20custom=20"initiator"=20property=20to=20query=20initiato?= =?UTF-8?q?r=20to=20boost=20performance=20even=20more=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2047412:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/?= =?UTF-8?q?DEV/CONV=5FV413:=20=20=20=20=20=20=20=20=20=20=20=20=2042252:?= =?UTF-8?q?=20(RECORD=20ONLY)=20Merged=20BRANCHES/DEV/V4.1-BUG-FIX=20to=20?= =?UTF-8?q?BRANCHES/DEV/CLOUD1=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2042233:=20Fix=20for=20ALF-16164=20Cloud=20monitor?= =?UTF-8?q?ing=20of=20SOLR=20is=20CPU=20intensive=20due=20to=20its=20repea?= =?UTF-8?q?ted=20use=20of=20the=20SOLR=20stats=20page=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20and=20?= =?UTF-8?q?related=20CLOUD-760=20Cloud=20monitoring=20of=20SOLR=20is=20CPU?= =?UTF-8?q?=20intensive=20due=20to=20its=20repeated=20use=20of=20the=20SOL?= =?UTF-8?q?R=20stats=20page=20=20=20=20=20=20=20=20=20=2047429:=20Merged?= =?UTF-8?q?=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2042200:=20Merged=20DEV/CLOUD1-B?= =?UTF-8?q?UG-FIX=20into=20DEV/CLOUD1:=20Record-only=20(r41650=20and=20r41?= =?UTF-8?q?674)=20=20=20=20=20=20=20=20=20=2047433:=20Merged=20BRANCHES/DE?= =?UTF-8?q?V/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20CLOUD-808:=20Fix=20for=20timer=20deploying?= =?UTF-8?q?=20MT-process=20when=20shared=20is=20required=20caused=20test?= =?UTF-8?q?=20to=20fail=20=20=20=20=20=20=20=20=20=2047435:=20Merged=20BRA?= =?UTF-8?q?NCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20Merged=20BRANCHES/DEV/CLOUD1=5FCO?= =?UTF-8?q?RS=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2043100:=20Update=20the=20salesforce=20amp?= =?UTF-8?q?=20to=20include=20the=20CORS=20Filter=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2043101:=20Update=20web.xml=20to=20?= =?UTF-8?q?enable=20to=20the=20CORS=20Filter=20with=20filter-mapping=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2043117:=20Add=20?= =?UTF-8?q?updated=20amp=20with=20removed=20CORS=20Filter.=20=20CORS=20Fil?= =?UTF-8?q?ter=20is=20now=20available=20in=203rd-party=20libs=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2043118:=20[CLOUD-724]?= =?UTF-8?q?=20Add=20CORS=20Filter=20jar=20to=203rd-party=20libs=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2043119:=20[CLOUD-72?= =?UTF-8?q?4]=20Add=20missing=20jar=20java-property-utils-1.6.jar=20to=203?= =?UTF-8?q?rd-party=20libs=20=20=20=20=20=20=20=20=20=2047485:=20Merged=20?= =?UTF-8?q?BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413:=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2044203:=20(RECORD=20ONLY)=20Merged?= =?UTF-8?q?=20BRANCHES/V4.1=20to=20BRANCHES/DEV/CLOUD1=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2044200:=20Probable=20fix?= =?UTF-8?q?=20for=20=20=20=20=20ALF-16895=20SOLR:=20Cannot=20find=20files?= =?UTF-8?q?=20after=20restart=20and=20reindex=20solr=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2044276:=20(RECORD=20ONLY)=20Merged=20BRANCHES/?= =?UTF-8?q?V4.1=20to=20BRANCHES/DEV/CLOUD1=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2044275:=20Part=202=20for=20ALF-16895?= =?UTF-8?q?=20SOLR:=20Cannot=20find=20files=20after=20restart=20and=20rein?= =?UTF-8?q?dex=20solr=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20-=20fix=20initial=20cache=20state=20?= =?UTF-8?q?to=20cope=20with=20duplicate=20leaf/aux=20doc=20entries.=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2044314:=20(RECORD=20ONLY)=20Mer?= =?UTF-8?q?ged=20BRANCHES/V4.1=20to=20BRANCHES/DEV/CLOUD1=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2044312:=20Part?= =?UTF-8?q?=203=20for=20ALF-16895=20SOLR:=20Cannot=20find=20files=20after?= =?UTF-8?q?=20restart=20and=20reindex=20solr=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20fix?= =?UTF-8?q?=20incremental=20cache=20state=20to=20cope=20with=20duplicate?= =?UTF-8?q?=20leaf/aux=20doc=20entries.=20=20=20=20=20=20=20=20=20=2047523?= =?UTF-8?q?:=20Merged=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV41?= =?UTF-8?q?3:=20=20=20=20=20=20=20=20=20=20=20=20=2044573:=20(RECORD=20ONL?= =?UTF-8?q?Y)=20Merged=20BRANCHES/DEV/CLOUD1=5FSP=20to=20BRANCHES/DEV/CLOU?= =?UTF-8?q?D1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2044572:?= =?UTF-8?q?=20Clean=20up=20of=20unused=20files.=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2044576:=20(RECORD=20ONLY)=20Merged=20BRANCHES/DEV/CL?= =?UTF-8?q?OUD1=5FCORS=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2044518:=20[CLOUD-955]=20Change=20C?= =?UTF-8?q?ORS=20filter-mapping=20to=20use=20servelet=20instead=20of=20url?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2044691:=20(RECORD=20ONLY)?= =?UTF-8?q?=20Merged=20BRANCHES/DEV/CLOUD1=5FCORS=20to=20BRANCHES/DEV/CLOU?= =?UTF-8?q?D1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2044688:?= =?UTF-8?q?=20(RECORD=20ONLY)=20Rebase=20CLOUD1=5FCORS=20with=20CLOUD1=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2044689:=20[CL?= =?UTF-8?q?OUD-1072]=20Add=20public=20api=20url=20to=20CORS=20filter=20map?= =?UTF-8?q?ping.=20=20Move=20CORS=20filter=20mapping=20to=20live=20above?= =?UTF-8?q?=20the=20publicapi=20filter=20mappings.=20=20OPTIONS=20calls=20?= =?UTF-8?q?made=20to=20the=20CORS=20filter=20should=20be=20evaluated=20bef?= =?UTF-8?q?ore=20Layer7=20authentication.=20=20=20=20=20=20=20=20=20=20475?= =?UTF-8?q?48:=20Merged=20DEV/CLOUD2=20to=20DEV/CONV=5FV413=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2046931:=20Overriding=20form=20runtime's?= =?UTF-8?q?=20submissionUrl=20using=20extesnion=20point=20in=20FormUIGet?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2046984:=20Overriding=20en?= =?UTF-8?q?tire=20sent-invites.js=20(instead=20of=20modifying=20the=20core?= =?UTF-8?q?=20slingshot=20code)=20with=20a=20copy=20of=20the=20core=20code?= =?UTF-8?q?=20modified=20to=20work=20with=20the=20cloud=20invite=20apis.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2046986:=20Overriding=20he?= =?UTF-8?q?lp=20pages=20config=20in=20cloud-config.xml=20(rather=20than=20?= =?UTF-8?q?modifying=20the=20core=20files!)=20=20=20=20=20=20=20=20=20=204?= =?UTF-8?q?7553:=20Merged=20DEV/CLOUD2=20to=20DEV/CONV=5FV413=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2047421:=20Overriding=20entire=20sent-?= =?UTF-8?q?invites.get=20ftl=20&=20properties=20(instead=20of=20modifying?= =?UTF-8?q?=20the=20core=20slingshot=20code)=20with=20a=20copy=20of=20the?= =?UTF-8?q?=20core=20code=20modified=20to=20fit=20the=20cloud=20requiremen?= =?UTF-8?q?ts.=20=20=20=20=20=20=20=20=20=20=20=20=2047442:=20Add=20web=20?= =?UTF-8?q?overlay=20for=20share=20+=20tune=20embedded=20librairies=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2047455:=20Add=20dependency=20on?= =?UTF-8?q?=20jetty-webapp=20to=20compile=20the=20tests=20=20=20=2048115:?= =?UTF-8?q?=20CONV:=20Fix=20cache=20defs=20(propertyUniqueContextCache=20&?= =?UTF-8?q?=20siteNodeRefCache)=20=20=20=2048117:=20Merged=20DEV/CONV=5FV4?= =?UTF-8?q?13=20to=20DEV/CONV=5FHEAD=20=20=20=20=20=20=20=20=2046959:=20Me?= =?UTF-8?q?rged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV4?= =?UTF-8?q?13=20=20=20=20=20=20=20=20=20=20=20=2035790:=20Merged=20BRANCHE?= =?UTF-8?q?S/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031624:=20Resolve=20THOR-302:=20transformat?= =?UTF-8?q?ion-client-1.0.0-SNAPSHOT.jar=20not=20in=20alfresco/WEB-INF/lib?= =?UTF-8?q?:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031632:=20More=20r?= =?UTF-8?q?eliable=20test,=20hopefully=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031644:=20Fix=20unreported=20issue=20in=20aws-context.x?= =?UTF-8?q?ml.sample=20(not=20well-formed=20XML)=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031645:=20Customized=20invite=20links=20to?= =?UTF-8?q?=20use=20"cloud=20dialog"=20instead=20of=20"invite=20page"=20fo?= =?UTF-8?q?r=20the=20following=20components:=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031662:=20New=20Analytics=20events=20and=20tests?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031663:=20New=20Ana?= =?UTF-8?q?lytics=20events=20and=20tests=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031678:=20Addition=20of=20isExternal=20data=20to=20s?= =?UTF-8?q?ite=20membership=20webscripts.=20=20=20=2048118:=20Merged=20DEV?= =?UTF-8?q?/CONV=5FV413=20to=20DEV/CONV=5FHEAD=20=20=20=20=20=20=20=20=204?= =?UTF-8?q?6960:=20Merged=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV?= =?UTF-8?q?/CONV=5FV413=20=20=20=20=20=20=20=20=20=20=20=2035791:=20Merged?= =?UTF-8?q?=20BRANCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031684:=20THOR-367=20-=20#1=20W?= =?UTF-8?q?ithin=20Site=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20-=20Remove=20all=20page=20components=20other=20than=20?= =?UTF-8?q?site=20dashboard,=20document=20library=20and=20members=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20Remove?= =?UTF-8?q?=20customize=20site=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031685:=20New=20lightweight=20webscript=20to=20retrieve=20use?= =?UTF-8?q?r/network=20metadata=20about=20the=20currently=20authenticated?= =?UTF-8?q?=20user=20in=20the=20current=20tenant.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031693:=20THOR-365:=20Private=20site=20cann?= =?UTF-8?q?ot=20be=20access=20(since=20surf-config=20is=20not=20imported)?= =?UTF-8?q?=20-=20causes:=20Could=20not=20resolve=20view=20with=20name=20.?= =?UTF-8?q?..=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031695:=20THOR-36?= =?UTF-8?q?7=20-=20#2=20Document=20Library=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20-=20remove=20Create=20Content.?= =?UTF-8?q?..=20menu=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20-=20remove=20actions:=20manage=20aspects,=20change=20t?= =?UTF-8?q?ype,=20publish,=20manage=20rules=20(for=20folders)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20-=20document?= =?UTF-8?q?-details=20page:=20remove=20publishing=20history=20panel=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031697:=20For=20reference?= =?UTF-8?q?=20only:=20update=20description=20of=20cmis/test=20webscript=20?= =?UTF-8?q?=20=20=2048119:=20Merged=20BRANCHES/DEV/CONV=5FV413=20to=20BRAN?= =?UTF-8?q?CHES/DEV/CONV=5FHEAD:=20=20=20=20=20=20=20=20=2046972:=20Fix=20?= =?UTF-8?q?test=20fallout=20(re:=20THOR-293)=20=20=20=2048120:=20Merged=20?= =?UTF-8?q?BRANCHES/DEV/CONV=5FV413=20to=20BRANCHES/DEV/CONV=5FHEAD:=20(re?= =?UTF-8?q?po=20pre-merge)=C2=A7=20=20=20=20=20=20=20=20=2047001:=20Merged?= =?UTF-8?q?=20from=20BRANCHES/DEV/CLOUD2=20to=20BRANCHES/DEV/CONV=5FV413?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=2035798:=20Merged=20BRA?= =?UTF-8?q?NCHES/DEV/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=2031805:=20Adding=20utility=20me?= =?UTF-8?q?thod=20to=20our=20CollectionUtils=20class=20that=20I=20need=20a?= =?UTF-8?q?s=20part=20of=20pending=20invitations=20work=20(THOR-373).=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031809:=20Parame?= =?UTF-8?q?terized=20signup=20url=20&=20email=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031812:=20THOR-373=20Pending=20invitatio?= =?UTF-8?q?ns.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031814:=20?= =?UTF-8?q?Made=20changes=20to=20way=20aid=20is=20captured=20ready=20for?= =?UTF-8?q?=20allowing=20events=20to=20override=20aid=20if=20needed=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031820:=20Mapping?= =?UTF-8?q?=20of=20network=20admin=20to=20system=20admin=20part=201:=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=2035801:Merged=20BRANCHES/DEV?= =?UTF-8?q?/THOR1=20to=20BRANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=2031829:=20Fixed=20THOR-352=20"Incorrect?= =?UTF-8?q?=20validation=20of=20emails=20on=20"Forgot=20Password"=20page"?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031830:=20(RE?= =?UTF-8?q?CORD=20ONLY)=20Exclude=20ExportDbTest;=20issues=20with=20MySQL?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031831:=20(RE?= =?UTF-8?q?CORD=20ONLY)=20Merged=20HEAD=20to=20BRANCHES/DEV/THOR1:=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20317?= =?UTF-8?q?84:=20Fix=20up=20unit=20test.=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031833:=20Email=20validation=20now=20allows=20?= =?UTF-8?q?7=20character=20long=20top=20level=20domain=20(so=20we=20can=20?= =?UTF-8?q?do=20tests=20with=20example)=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031834:=20New=20form=20colors=20for=20invalid?= =?UTF-8?q?=20&=20mandatory=20fields=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031837:=20THOR-327=20-=20remove=20bootstrapped=20?= =?UTF-8?q?guest=20/=20guest@=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031838:=20THOR-327=20-=20remove=20bootstrapped=20?= =?UTF-8?q?guest=20/=20guest@=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031844:=20Added=20missing=20headers=20to=20Java?= =?UTF-8?q?=20files.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20318?= =?UTF-8?q?45:=20Mapping=20of=20network=20admin=20to=20system=20admin=20pa?= =?UTF-8?q?rt=202:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031846?= =?UTF-8?q?:=20Addition=20of=20very=20basic=20test=20script=20for=20the=20?= =?UTF-8?q?Script=20API=20of=20AnalyticsService.=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2035803:=20Merged=20BRANCHES/DEV/THOR1=20to=20B?= =?UTF-8?q?RANCHES/DEV/CLOUD1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=2031853:=20Forms=20refactor=20first=20cut=20-=20for=20re?= =?UTF-8?q?view=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031855:?= =?UTF-8?q?=20THOR-387.=20Analytics=20event=20for=20user=20activation=20is?= =?UTF-8?q?=20sent.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203185?= =?UTF-8?q?8:=20THOR-387.=20Fixing=20a=20corner-case=20bug=20in=20SendAnal?= =?UTF-8?q?yticsRequest.=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031863:=20(RECORD=20ONLY)=20Merged=20HEAD=20to=20BRANCHES/DEV?= =?UTF-8?q?/THOR1:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=2031841:=20Build=20Fix=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=2031868:=20THOR-361:=20Fix=20/service/index?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031881:=20THO?= =?UTF-8?q?R-387.=20Adding=20analytics=20event=20for=20site=20invitation.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031882:=20THO?= =?UTF-8?q?R-387.=20Fixing=20analytics=20event=20for=20site=20invitation.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031883:=20THO?= =?UTF-8?q?R-66:=20disable=20some=20of=20the=20/alfresco=20(web.xml)=20ser?= =?UTF-8?q?vlet=20mappings=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=2031884:=20THOR-387.=20Analytic=20event=20callouts=20for=20sit?= =?UTF-8?q?e=20invitation=20response.=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031899:=20Revert=20solrcore.properties=20checkin?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031900:=20THO?= =?UTF-8?q?R-249:=20override=20edition=20interceptor=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031901:=20Fix=20for=20THOR-396.=20?= =?UTF-8?q?Spelling=20mistake=20on=20signup=20screen.=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=2031902:=20Resolve=20THOR-251:=20Up?= =?UTF-8?q?date=20the=20Help=20URLs=20for=20Cloud=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=2031904:=20Resolve=20THOR-403:=20-syst?= =?UTF-8?q?em-=20tenant=20not=20found=20logged=20from=20server=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=2031918:=20Create=20site?= =?UTF-8?q?=20form=20tweak=20(manual=20form.validate()=20call=20required?= =?UTF-8?q?=20since=20javascript=20is=20changing=20a=20another=20fields=20?= =?UTF-8?q?value)=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031919:?= =?UTF-8?q?=20Logout=20page=20refactoring=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=2031925:=20Create=20site=20now=20resets=20form?= =?UTF-8?q?=20before=20show=20using=20forms-runtime's=20new=20"reset"=20me?= =?UTF-8?q?thod=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=2031926:?= =?UTF-8?q?=20Disable=20flash=20upload=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=2031927:=20THOR-363:=20increase=20initial=20file?= =?UTF-8?q?=20quota=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=203193?= =?UTF-8?q?0:=20Updated=20SimpleDB=20service=20so=20you=20can=20set=20the?= =?UTF-8?q?=20SimpleDB=20domain=20to=20record=20events=20too?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 47003: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35804: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31933: THOR-387. Analytics. Added analytic call for account registration (the initial signup, not the activation, which was added previously). This adds a new mandatory parameter to the signup webscript: "source" as well as various new optional parameters. The same parameter is now mandatory on the RegistrationService. Impacts on test code. Changed the rest-client .rcq file to show new required parameter. Changed AnalyticsProperties to take the Object wrappers for primitives as these are optional and so we need to be able to pass null. Added a new (hidden) field to Erik's signup Share page to send an appropriate value for the signup. 31939: THOR-404: disable JBPM 31943: THOR-387. Analytics. I've overridden upload.post.js to add analytics data for file uploads. 31946: Fixed THOR-385 "Account summary file usage bar does not display for any theme other than the default theme" 31947: Fixed THOR-308 "Invite user drop-down works incorrectly" 31948: Resolve THOR-384: It is impossible to create user administrator@'domain': 31949: Follow-up fix for case sensitive user names 31953: THOR-311: It is impossible to create workflow when 'Send Email Notifications' flag is checked: 31959: Removing change-password override since user shall be able to change his password 31961: Fix tests after recent username/email address changes 31966: Grey Theme 31979: Dropping Analytics logging level down to 'warn' from 'debug'. 31982: Fixed THOR-419 "UI edits required" 31983: Fixed THOR-419 "UI edits required" part 2 32003: THOR-422. Spurious error logging during signup/registration (not activation). This was because the analytics event action code assumed the user exists, which they don't do at registration, of course. 32004: Resolve undefined undefined seen in invite signup dialog 32006: Restricted tentant component now displays dialog instead of gray page 32007: THOR-300: fix AWS config 32013: Fixed THOR-353 "No validation for the fields on the "Reset Password" page" 32014: Fixed THOR-423 "Removing the yellow "Welcome to your dashboard, firstname, lastname" causes error" 32018: Made sure new cloud theme (greyTheme) also has new theme border & bgs (making the account quota being displayed) 48122: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47007: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: 35817: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32250: (RECORD ONLY) Merged /HEAD to BRANCHES/DEV/THOR1: (ok'ed with DC/DG) 31750: Solr: Fix owner Id cache 31751: Fix for ALF-11104: add authenticated user to authorisations list in PermissionService + fix inconsistency in AuthorityService 31760: Correct Fix for SOLR owner ID cache 32172: Fixes for: ALF-11521 Protect SOLR running against the wrong Alfresco DB ALF-11602 Solr Core Tracker - does not need to re-init CMIS dictionary (when there are no model changes) ALF-11621 SOLR old versions of tracked models are not getting deleted when models are updated 32234: Fix for ALF-11568 SOLR indexing is ignoring properties that are indexed but not tokenised and not stored - was WCMQS navigation is broken 32256: THOR-488. Tidy up account types. 32258: Login analytics event. 32260: Reduce logging on startup for enabled tenants (see also THOR-475 / THOR-81) 32262: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: 32139: Fix for ALF-11599 - Section ''Others are Editing'' shows documents that should not be present 48123: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: (repo pre-merge) 47038: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35811: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32019: Merged rev 32016 from THORSURF1 32021: THOR-428: Fix activity feed email notifications (to contain network/tenant ctx) 32024: Fixed "THOR-424 'Upload File' button is disabled in FF for the second and futher uploads" 32026: Restricted tenant page now has link back to users home dashboard so he doesn't feel stuck 32029: Fixed GetRequest test to ignore uid's that aren't emails (like admin) 32030: THOR-310: Override getCacheKey method from AbstractCachedViewResolver to ensure that each tenant gets their own cached copy of each Share page (this ensures that nested Component config gets processed for all tenants) 32031: Resolve THOR-417 Workflow notification emails do not take into account tenant in their urls back to Share 47039: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35812: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32041: Label changes according to Kathryn's "UI Text_scenario 5.docx" 32052: THOR-405: Fix 'contentstore.deleted' to be on S3 (albeit co-mingled) 32058: Removed unnecessary borders from profile pages 32065: Fix build issue where cloud share war was not being cleaned before build 32066: Fix those pesky solrcore properties 32071: THOR-461: fix following email notification (to contain network/tenant ctx) 32076: Fix to disable error on unit tests 32077: Added logging to NullPointerException fix 35814: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32103: Finally! A fix for THOR-193. :) 32119: Fix for setting theme as network admin 32120: Improved text on upgrade account page 32124: Refactored CloudInvitationService Integration Tests to allow for easier expansion and then I expanded. 32130: Fix for THOR-457. Already have an account email template needs updating/fixing. 32135: THOR-464 Fix "ThumbnailRegistry init does not scale with # of tenants" 32140: Apply Beta logos and adjust about dialog for cloud 35815: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32144: THOR-438: Latest Spring Surf libs (fix relative URI login redirect problem caused by un-encoded URI) 32147: THOR-475 - improvement(s) to trim time to create tenant 32148: THOR-475 - improvement(s) to trim time to create tenant 32154: GreyTheme updates 32157: THOR-430: Forgot password dialog: UI text not what was suggested 32159: GreyTheme updates 32174: THOR-454 - User can find content stored in Company Home/Data Dictionary via Advanced Search 32176: Signup page now cloud.alfresco.com 32179: THOR-475 - improvement(s) to trim time to create tenant 32184: Remove jargon from workflow names and descriptions 32185: Pesky solrcore.properties 35816: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32188: THOR-478: Updated Spring Surf libs - fixed relative URL redirect after login including support for @ symbol in URL 32195: Fix for THOR-379. Pending invitations UI show invitee emails as links to profile pages - even for non-existent users. Added yet more data to the CloudInvitation REST API: inviteeIsMember which tells caller whether the invitee is already a member of the tenant in which the invitation is running. Returning this flag through the Java API & REST API Tweaks to the Share JS so that it renders a for invitees who are not members and an for those who are. 32198: Replace workflow text with task related text 32202: Resolve THOR-481: Moving or copying content always shows error popup but always succeeds 32204: Build fix 32238: THOR-290: Configurable google-analytics tracking code script insertion 32239: Tidying up some UI text. Missing apostrophes, invitation instead of invite. 32241: THOR-471: Added GetSatisfaction feedback widget 35818: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32266: Addition of createSite analytics recording. 32268: THOR-505: Disable (turnoff autostart) of unused subsystems 32270: Adding in some theme colors that dissapeared (will make the quota bar get displayed again) 32272: Resolve THOR-354: (None) displayed for network administrators 32273: THOR-499: New Relic monitoring updates 32279: Implemented THOR-508 "Accept terms & conditions checkbox & link on the complete profile pages" 32280: Fixed THOR-474 "Password Strength indicator does not conform with other leading website password indicators" 48125: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: (repo pre-merge) 47053: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32377: (RECORD ONLY) THOR-565: fix unfortunate type that affects activity permissions (for connected users - either via site membership or followers) 32378: CollectionUtils method for collection intersection. Should be merged to HEAD. 32383: THOR-572: remove unused JBPM servlets (deployprocess, workflowdefinitionimage) 32384: Fixed THOR-549 "Google Analytics Installed but not seeing any events raised on GA reports" 32389: Fix for THOR-567 "userprofile broken" 32401: THOR-525 - fix MT-specific issue (deleting site does not clear associated activities within tenant) 32409: THOR-66: disable WebDAVServlet (does not need to load-on-startup) + a few others 32414: Theme updates from linton 32423: Fixed THOR-661 "Limit number of simultaneous connections in drag n drop upload" 32424: THOR-81: support for signup/activate scaling tests 48126: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47058: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts (not mergeinfo/slingshot/web-framework-commons/3rd-party) 35827: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35828: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35829: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35830: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35831: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35832: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 48129: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47067: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35844: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35845: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35846: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35847: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35848: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35849: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35850: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35853: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35854: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35855: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 47069: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35860: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33057: Refactored Slingshot overrides so that they are now in the Thor-Share private module. This has been done to reduce conflict issues when merging back into HEAD. The overrides are now in the correct locations (the only files that could not be moved to the private module are urlrewrite.xml and surf.xml). 35870: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: (part 1 - repository project) 33022: THOR-662: Email templates should load/resolve (initially) from classpath 47071: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35877: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33090: ALF-10826: hidden aspect 33091: THOR-416: fix surf-config folder (appears where it shouldn't) 33093: Sweep through email templates. 47072: Fix merge error (FeedCleaner) 47073: Fix merge error (WorkflowTestSuite) 47074: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35881: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33104: Tweak to invitation email template 33112: Refactored impl of THOR-694 so that content limit of 25Mb is on by default in THOR for both local FS and S3-based FS. Changed ContentLimitProvider bean to take String limit, rather than long - to allow empty string value on core Alfresco. Set the limit to the empty string in core Alfresco, which means 'no limit'. Applied the limit always. Set the limit to 25Mb in Thor/alfresco-global.properties Fixed a minor bug in error reporting due to previous exception renaming. 47076: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35885: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33134: THOR-874: Updated Surf libs Fixes ArrayIndexOutOfBoundsException caused by multiple threads apply i18n extensions to a WebScript 33135: Missing WebScripts sources JAR from r33134 33153: Resolve THOR-551: Password Hashes Need Review 33154: Update to latest email blacklist 33155: Fixed THOR-534 "Login Box shows scroll bars" 33156: Build fix for tests failing due to recent password changes 33157: Build fix for updated email blacklist 33172: THOR-776: Re-implement Share override as guided by Erik 33173: THOR-831: Text in Someone 35886: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33174: Latest SpringSurf libs - improved RemoteClient reused of connections per request thread. 33176: THOR-833: Search: clicking on All Sites returns no results 48131: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47098: (RECORD ONLY) Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35895: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 34105: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/THOR1: 33267: (RECORD ONLY) Created branch THOR1_SPRINTS (from THOR1 r33255) 33269: Snapshot of simple redeploy shell script (for AWS mini-dev/test env) 33272: JMeter test script 34106: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/THOR1: 33313: THOR-928: Added caching for i18n bundles provided by extensibility modules (latest Surf libs, r980) 48133: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47097: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35906: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35907: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 47099: Fix merge/compile error. 47103: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35913: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35914: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35915: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35916: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35917: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 47111: Fix merge error 47115: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35930: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35933: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35934: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 47132: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 36053: 1st pass at upgrading to latest Spring Surf 36059: Fix CloudInvitationService tests for cloud1 47133: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: 48135: Merged DEV/CONV_V413 to DEV/CONV_HEAD 46977: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35792: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31724: Can't compare pages using page.url.uri anymore since that doesn't include the tentant, now skips that part of the url and uses page.id instead. 31733: Add account info to user network web script 31736: Refactored RegistrationServiceImpl.promote... so that it uses the presence of cloud:personExternal aspect to prevent promotion of external users rather than account-based data. 35794: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31744: Account relates to url & various Share features now hidden in cloud 31746: 1/5 for THOR-341 "F147: Share features are disabled for external network member" 35796:Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31756: THOR-265: Currently the first user to sign up to a dmain becomes the domain admin, and can view the full admin console. Is this going to change? 31771: Added stub for SimpleDBAnalytics Service 31772: Final interfaces and integration with SimpleDB for Analytics 31774: Modified landing_time key for MixPanel 31776: Implemented #3 for THOR-341 "F147: Share features are disabled for external network member" 31777: Resolve test classpath since introduction of new thor libs 31779: Implemented #3 for THOR-341 "F147: Share features are disabled for external network member" part 2 31781: Resolve issue getting access to account settings when network admin of paid business account 31783: Implemented #2 for THOR-341 "F147: Share features are disabled for external network member" 31794: Minor changes after review with DavidC and NeilM 31797: Collaboration title improvement: Now hiding the html elements used to build the menu until the menu is created so ui doesn't bump and look ugly. 31799: Part #6 of THOR-367 "F60: Remove Share features not required for Cloud" - 6) Document Selectors - root is Sites folder 31801: Removed ugly "extra" borders around some of the input fields in the user profile form 31802: Fixed part #7 of THOR-367 "F60: Remove Share features not required for Cloud" - 7) Edit Profile - remove edit email from edit profile form 31804: Fixed part #5 of THOR-367 "F60: Remove Share features not required for Cloud" - 5) Move... / Copy... dialog - remove repository - remove my user home 48136: Merged DEV/CONV_V413 to DEV/CONV_HEAD 47001: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35798: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31805: Adding utility method to our CollectionUtils class that I need as part of pending invitations work (THOR-373). 31809: Parameterized signup url & email 31812: THOR-373 Pending invitations. 31814: Made changes to way aid is captured ready for allowing events to override aid if needed 31820: Mapping of network admin to system admin part 1: 35801:Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31829: Fixed THOR-352 "Incorrect validation of emails on "Forgot Password" page" 31830: (RECORD ONLY) Exclude ExportDbTest; issues with MySQL 31831: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: 31784: Fix up unit test. 31833: Email validation now allows 7 character long top level domain (so we can do tests with example) 31834: New form colors for invalid & mandatory fields 31837: THOR-327 - remove bootstrapped guest / guest@ 31838: THOR-327 - remove bootstrapped guest / guest@ 31844: Added missing headers to Java files. 31845: Mapping of network admin to system admin part 2: 31846: Addition of very basic test script for the Script API of AnalyticsService. 35803: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31853: Forms refactor first cut - for review 31855: THOR-387. Analytics event for user activation is sent. 31858: THOR-387. Fixing a corner-case bug in SendAnalyticsRequest. 31863: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: 31841: Build Fix 31868: THOR-361: Fix /service/index 31881: THOR-387. Adding analytics event for site invitation. 31882: THOR-387. Fixing analytics event for site invitation. 31883: THOR-66: disable some of the /alfresco (web.xml) servlet mappings 31884: THOR-387. Analytic event callouts for site invitation response. 31899: Revert solrcore.properties checkin 31900: THOR-249: override edition interceptor 31901: Fix for THOR-396. Spelling mistake on signup screen. 31902: Resolve THOR-251: Update the Help URLs for Cloud 31904: Resolve THOR-403: -system- tenant not found logged from server 31918: Create site form tweak (manual form.validate() call required since javascript is changing a another fields value) 31919: Logout page refactoring 31925: Create site now resets form before show using forms-runtime's new "reset" method 31926: Disable flash upload 31927: THOR-363: increase initial file quota 31930: Updated SimpleDB service so you can set the SimpleDB domain to record events too 48137: Merged DEV/CONV_V413 to DEV/CONV_HEAD 47003: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35804: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 31933: THOR-387. Analytics. Added analytic call for account registration (the initial signup, not the activation, which was added previously). This adds a new mandatory parameter to the signup webscript: "source" as well as various new optional parameters. The same parameter is now mandatory on the RegistrationService. Impacts on test code. Changed the rest-client .rcq file to show new required parameter. Changed AnalyticsProperties to take the Object wrappers for primitives as these are optional and so we need to be able to pass null. Added a new (hidden) field to Erik's signup Share page to send an appropriate value for the signup. 31939: THOR-404: disable JBPM 31943: THOR-387. Analytics. I've overridden upload.post.js to add analytics data for file uploads. 31946: Fixed THOR-385 "Account summary file usage bar does not display for any theme other than the default theme" 31947: Fixed THOR-308 "Invite user drop-down works incorrectly" 31948: Resolve THOR-384: It is impossible to create user administrator@'domain': 31949: Follow-up fix for case sensitive user names 31953: THOR-311: It is impossible to create workflow when 'Send Email Notifications' flag is checked: 31959: Removing change-password override since user shall be able to change his password 31961: Fix tests after recent username/email address changes 31966: Grey Theme 31979: Dropping Analytics logging level down to 'warn' from 'debug'. 31982: Fixed THOR-419 "UI edits required" 31983: Fixed THOR-419 "UI edits required" part 2 32003: THOR-422. Spurious error logging during signup/registration (not activation). This was because the analytics event action code assumed the user exists, which they don't do at registration, of course. 32004: Resolve undefined undefined seen in invite signup dialog 32006: Restricted tentant component now displays dialog instead of gray page 32007: THOR-300: fix AWS config 32013: Fixed THOR-353 "No validation for the fields on the "Reset Password" page" 32014: Fixed THOR-423 "Removing the yellow "Welcome to your dashboard, firstname, lastname" causes error" 32018: Made sure new cloud theme (greyTheme) also has new theme border & bgs (making the account quota being displayed) 48147: CONV: fix merge issue - remove duplicate prop def (contentLimitProvider) 48148: Merged DEV/CONV_V413 to DEV/CONV_HEAD merge fix for r48072 48149: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47111: Fix merge error 47115: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 35930: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35933: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35934: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 47132: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: - pre-merge of repo parts 36053: 1st pass at upgrading to latest Spring Surf 36059: Fix CloudInvitationService tests for cloud1 47133: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: 48150: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: (effectively RECORD ONLY - no changes) 47173: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: 36232: MT - fix pop of tenant ctx (to match push) 48154: Merged DEV/CONV_V413 to DEV/CONV_HEAD 47038: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35811: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32019: Merged rev 32016 from THORSURF1 32021: THOR-428: Fix activity feed email notifications (to contain network/tenant ctx) 32024: Fixed "THOR-424 'Upload File' button is disabled in FF for the second and futher uploads" 32026: Restricted tenant page now has link back to users home dashboard so he doesn't feel stuck 32029: Fixed GetRequest test to ignore uid's that aren't emails (like admin) 32030: THOR-310: Override getCacheKey method from AbstractCachedViewResolver to ensure that each tenant gets their own cached copy of each Share page (this ensures that nested Component config gets processed for all tenants) 32031: Resolve THOR-417 Workflow notification emails do not take into account tenant in their urls back to Share 47039: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35812: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32041: Label changes according to Kathryn's "UI Text_scenario 5.docx" 32052: THOR-405: Fix 'contentstore.deleted' to be on S3 (albeit co-mingled) 32058: Removed unnecessary borders from profile pages 32065: Fix build issue where cloud share war was not being cleaned before build 32066: Fix those pesky solrcore properties 32071: THOR-461: fix following email notification (to contain network/tenant ctx) 32076: Fix to disable error on unit tests 32077: Added logging to NullPointerException fix 35814: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32103: Finally! A fix for THOR-193. :) 32119: Fix for setting theme as network admin 32120: Improved text on upgrade account page 32124: Refactored CloudInvitationService Integration Tests to allow for easier expansion and then I expanded. 32130: Fix for THOR-457. Already have an account email template needs updating/fixing. 32135: THOR-464 Fix "ThumbnailRegistry init does not scale with # of tenants" 32140: Apply Beta logos and adjust about dialog for cloud 35815: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32144: THOR-438: Latest Spring Surf libs (fix relative URI login redirect problem caused by un-encoded URI) 32147: THOR-475 - improvement(s) to trim time to create tenant 32148: THOR-475 - improvement(s) to trim time to create tenant 32154: GreyTheme updates 32157: THOR-430: Forgot password dialog: UI text not what was suggested 32159: GreyTheme updates 32174: THOR-454 - User can find content stored in Company Home/Data Dictionary via Advanced Search 32176: Signup page now cloud.alfresco.com 32179: THOR-475 - improvement(s) to trim time to create tenant 32184: Remove jargon from workflow names and descriptions 32185: Pesky solrcore.properties 35816: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32188: THOR-478: Updated Spring Surf libs - fixed relative URL redirect after login including support for @ symbol in URL 32195: Fix for THOR-379. Pending invitations UI show invitee emails as links to profile pages - even for non-existent users. Added yet more data to the CloudInvitation REST API: inviteeIsMember which tells caller whether the invitee is already a member of the tenant in which the invitation is running. Returning this flag through the Java API & REST API Tweaks to the Share JS so that it renders a for invitees who are not members and an for those who are. 32198: Replace workflow text with task related text 32202: Resolve THOR-481: Moving or copying content always shows error popup but always succeeds 32204: Build fix 32238: THOR-290: Configurable google-analytics tracking code script insertion 32239: Tidying up some UI text. Missing apostrophes, invitation instead of invite. 32241: THOR-471: Added GetSatisfaction feedback widget 35818: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32266: Addition of createSite analytics recording. 32268: THOR-505: Disable (turnoff autostart) of unused subsystems 32270: Adding in some theme colors that dissapeared (will make the quota bar get displayed again) 32272: Resolve THOR-354: (None) displayed for network administrators 32273: THOR-499: New Relic monitoring updates 32279: Implemented THOR-508 "Accept terms & conditions checkbox & link on the complete profile pages" 32280: Fixed THOR-474 "Password Strength indicator does not conform with other leading website password indicators" 47053: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32377: (RECORD ONLY) THOR-565: fix unfortunate type that affects activity permissions (for connected users - either via site membership or followers) 32378: CollectionUtils method for collection intersection. Should be merged to HEAD. 32383: THOR-572: remove unused JBPM servlets (deployprocess, workflowdefinitionimage) 32384: Fixed THOR-549 "Google Analytics Installed but not seeing any events raised on GA reports" 32389: Fix for THOR-567 "userprofile broken" 32401: THOR-525 - fix MT-specific issue (deleting site does not clear associated activities within tenant) 32409: THOR-66: disable WebDAVServlet (does not need to load-on-startup) + a few others 32414: Theme updates from linton 32423: Fixed THOR-661 "Limit number of simultaneous connections in drag n drop upload" 32424: THOR-81: support for signup/activate scaling tests 48157: Merged DEV/CONV_V413 to DEV/CONV_HEAD Fixing merge issue from r48135 48158: Merged DEV/CONV_V413 to DEV/CONV_HEAD (RECORD ONLY) 47046: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32281: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1 (ok'd with DC): 32242: ALF-11664 Moderated sites should use site.public.group (from SysAdminParams) for setting the group with general access, as Public sites already do, rather than hard coding the EVERYONE group 32283: Added "guest" to the list of blocked usernames, thereby showing failure to register guest@tenant.com, rather than allowing it and failing to activate the account later. 32285: THOR-505: Disable (turnoff autostart) of unused subsystems 32286: Fix NPE in AnalyticsProperties when empty json provided 32287: THOR-508 "Accept terms & conditions checkbox & link on the complete profile pages" 32289: Miscellaneous changes to account types & classes. 32290: Change "recent activities" email notification interval from hourly to daily 32292: Resolve THOR-516: Check all email template URLs point to cloud.alfresco.com not www.alfresco.me 32293: THOR-517. Insert Signup Analytics Event into Site Invite process. 32305: Fixed THOR-306 "Invite user autocomplete not working correctly" 32308: THOR-529: Red "No items" in doc lib when adding 1st document into a x-network site (WebDAV error in log) 32309: Fixed THOR-306 "Invite user autocomplete not working correctly" 32314: THOR-520: Change workflow in tooltip text to task 32318: THOR-532: Improve auto-generate of home site shortname (in case of clash) 32339: Updated analytics events to include parameter 32342: Additional debug logging as part of THOR-544. 32376: THOR-574: Accept invite while logged in displays 'you've declined...' message 48163: Merge CONV_V413 to CONV_HEAD 46713: Set Maven version in POM files to 4.1.3-CONV-SNAPSHOT 46741: Deploy SPP jar file (aka VTI) into Maven repository as well 47440 RECORD ONLY: Declare dependency on Surf 1.2.0-SNAPSHOT in Maven poms 47450 RECORD ONLY: Bring Chemistry OpenCMIS libs back into the wars 47579 RECORD ONLY: Switch Chemistry OpenCMIS version to a custom 0.8.0-20120706 47646: POM dependency: use 4.2-min version of netcdf rather than 4.2, which embeds an old commons-codec 47683: Create a jar holding the sharepoint config, for use with CLOUD2 47740 RECORD ONLY: Merge V4.1-BUG-FIX to CONV_V413 46360: ALF-17697: Create proper source jars, to deploy to Maven repository 47964: Filter servlet-api from dependencies 48166: Merged DEV/CONV_V413 to DEV/CONV_HEAD 47064: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35827: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32446: Fixed THOR-658 "File Upload Limits" 32455: Tweak logging (S3 exists check -> debug) 32462: Move tenant enabled check from low level services to web script entry point: 32467: THOR-666: Improve startTenants - do not need to re-update enable/disable flag on startup 32474: JMeter test script updates (#3) 32485: Fix to ensure the HTML upload POSTed response can return html content type. 32486: JMeter test script updates (#4) 47084: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35828: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32491: Fix account signup since change to tenant authentication (which is now slightly stricter: 32518: Fix for mixed cased usernames login problems 32523: Add db pool validate query 32546: Minor - remove unecessary call to getObjectDetails (to avoid calling twice for non-existent object) 32556: Fixed tenant url edge cases and followed up a fix started by DavidC for signup logins 32560: Fixed tenant url edge cases and followed up a fix started by DavidC for signup logins - part 2 35829: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32571: Resolve THOR-653: Uploads consume disk space in /var/cache/tomcat6/ 32572: Fixed THOR-563 "UI: CSS / layout issue on profile page" 35830:Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32577: THOR-682: refactor Tenant/S3 routing content store (it is now self-routing based on S3 content url) 32580: Added missing init-method attributes to key CachingContentStore components. 32583: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: 32321: ALF-11700: Possible to generate feed entries with malformed NodeRefs 32593: THOR-688 Analytics to support various URLs not just "website". Addition of optional sourceUrl paramater to account-signup analytics. 32603: Added file size limitation and hooked in html upload to the form validations w backgrounds and tooltips. 32629: THOR-199: Fix create user (activate) sometimes has to retry - due to: "Deadlock ... alfresco.permissions.insert_AclMember-Inline" 32654: THOR-692: Disable (auto) home folder creation 35831: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32661: Latest SpringSurf libs: 35832: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32666: Build: add cloud/cloud-share to ant clean-modules/clean 32672: Ensure that application context is available for TenantAlfrescoAuthenticator in TenantUserFactory 32675: THOR-536: Added TenantPageTypeViewResolver 32686: JMeter test script updates (#5) 32700: THOR-689: DevTest: 2 uploads failed (out of 10000) - missing retry ? 35844:Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32702: JMeter test script update 32718: THOR-691: Feed Notifier sends emails on startup of Server 32756: Fixed THOR-556 "Can't view members in a public Site" 35845: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: (+ resolved conflicts w/ 4.0.1) 32032: THOR-370: Add tenant-switching to /cmisatom (OpenCMIS-based v4.x impl => AlfrescoCmisService) 35846: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32759: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1 32757: Fix for ALF-9365 32761: Changed restricted tenant to appear as page not found 32763: THOR-792: Ensure that failed login returns to login page 32769: Updated networks icon 32770: Authentication updates: Unauthenticated requests to inaccessible tenants (either that don't exist or not authorized to access) will be prompted for authentication and if credentials are valid the "Page Not Found" page will be shown, but authentication will have completed and user can return to their home dashboard via link provided 32785: Fix for THOR-798 32789: THOR-796: reduce startup time (1000s of tenants) 35847: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32790: THOR-480: Spring Surf lib updates - ensure that i18n properties extensions degrade the specificity of the locale to ensure that no message keys are shown (unless the message genuinely doesn't exist) 32798: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: (fix for THOR-721) 32245: Unit tests for ALF-10343, with the problematic parts commented out pending a fix 32251: ALF-11664 site.public.group (via SysAdminParams.SitePublicGroup) should be used when updating site visibilities, as it is for creating sites 35848: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32805: Latest SpringSurf libs: 47085: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35849: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32807: Fixed invalid network switching URLs 32808: Updated Spring Surf libs to that revert invalid changes to relativeUri determining method 32837: Set Alfresco connector reconnect timeout to zero 32856: Fix for THOR-801. Trying to access the archive as 'admin' gives error. 35850: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 32858: (RECORD ONLY) Merged HEAD to BRANCHES/DEV/THOR1: (pull in some pre-reqs for cleaner merge of ALF-10826) 31864: ALF-10686 - Original modification date is lost when files are copied into Alfresco via CIFS 31934: Update stale File State Cache. 32068: ALF-10941 - CIFS Open file from excel 32097: build fix. 32131: ALF-10902 - No friendly notification occurs when Editor or Collaborator tries to delete content 32132: Open read-only for attributes only. 32182: ALF-10963 Cannot overwrite files on CIFS share with Notepad++ 32876: THOR-784: Fix 'Accounts API loading is very slow' (get page of accounts) 32939: THOR-480: Latest Spring Surf libs - fix i18n extensibility problems. 32948: THOR-859: Performance: Disable rules service 32953: THOR-863: Performance: loadUserByUsername -> isAdminAuthority 32959: (RECORD ONLY) Merged HEAD to DEV/THOR1 32958: (record-only) Merged Dev/THOR1 to HEAD 32945: Fix for ALF-12122 Some CMIS queries with SOLR are not returning correct results 35853: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33024: Fixed THOR-670 "Incorrect window title for 'Task History' page" 33027: Missing merge info for r32694 35854: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33033: Latest SpringSurf libs: 35855: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33038: Minor: do not bootstrap web script readme x2 into Data Dictionary (when creating tenant) 33039: Resolve THOR-839: Following webscripts doesn't set Content-Type response header 33040: Fixed THOR-817 "Issues with "invite user" email autocomplete field" 33041: Fixed THOR-789 "Mix of languages" 33042: Resolved THOR-849: Upload issue ? - Failed to get content ... (No such file or directory) ... x22 48169: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47176: (RECORD ONLY) Fix Eclipse .classpath to match Spring Surf libs 48170: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 48168: CONV: Fix NPE in get people CQ 48183: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47184: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 35989: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/THOR1: 34153: Minor: THOR-5: MT-aware immutable singletons (spp/vti) 34161: Prevent session timeout redirect problem resulting from clicking user link in activities feed 34183: Part one of THOR-1129. 34185: Part two of THOR-1129. The Thor-specific parts. 34199: Fix for THOR-106 a failing test case that was switched off. 34202: THOR-106 addendum. Editing build.xml to put the test class back in to the build. 34211: BM: sync ThorTest (additional coverage) 34308: Merged HEAD to THOR1_SPRINTS 34250: Fixed THOR-1137 "Make Spring Surf enable-auto-deploy-modules by default" 34540: Share UI - copyright should be 2012 (related to THOR-1015) 35286: Resolve THOR-1242: Update Beta Logo 48187: Merged DEV/CONV_V413 to DEV/CONV_HEAD 47086: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35860: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33057: Refactored Slingshot overrides so that they are now in the Thor-Share private module. This has been done to reduce conflict issues when merging back into HEAD. The overrides are now in the correct locations (the only files that could not be moved to the private module are urlrewrite.xml and surf.xml). 35870: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: (part 1 - repository project) 33022: THOR-662: Email templates should load/resolve (initially) from classpath 35877: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33090: ALF-10826: hidden aspect 33091: THOR-416: fix surf-config folder (appears where it shouldn't) 33093: Sweep through email templates. 35881: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33104: Tweak to invitation email template 33112: Refactored impl of THOR-694 so that content limit of 25Mb is on by default in THOR for both local FS and S3-based FS. Changed ContentLimitProvider bean to take String limit, rather than long - to allow empty string value on core Alfresco. Set the limit to the empty string in core Alfresco, which means 'no limit'. Applied the limit always. Set the limit to 25Mb in Thor/alfresco-global.properties Fixed a minor bug in error reporting due to previous exception renaming. 35885: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33134: THOR-874: Updated Surf libs Fixes ArrayIndexOutOfBoundsException caused by multiple threads apply i18n extensions to a WebScript 33135: Missing WebScripts sources JAR from r33134 33153: Resolve THOR-551: Password Hashes Need Review 33154: Update to latest email blacklist 33155: Fixed THOR-534 "Login Box shows scroll bars" 33156: Build fix for tests failing due to recent password changes 33157: Build fix for updated email blacklist 33172: THOR-776: Re-implement Share override as guided by Erik 33173: THOR-831: Text in Someone 47096: Fix merge compile issue 47100: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35906: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33054: THOR-796: slow startup time (on QA env with ~ 10k tenants) 33055: Implementation of THOR-694. File size upload limit within ContentStore. 35907: Merged BRANCHES/DEV/THOR1 to BRANCHES/DEV/CLOUD1: 33213: THOR-833: wip 33214: Allow for workflows which may have a reference to a repo based email template 33228: Added extension points for links in user profile toolbar 33230: Added extension points for links in user profile toolbar part 2 33232: Fixed THOR-907 "Remove Share functionality which allows access to people profiles outside of your site memberships" 33233: Fixed THOR-907 "Remove Share functionality which allows access to people profiles outside of your site memberships" part 2 33234: Extension points in members bar now ft the pattern of user profile toolbar. 33236: Fixed THOR-907 "Remove Share functionality which allows access to people profiles outside of your site memberships" part 3 33241: THOR-908 - wip 33243: THOR-908 / THOR-64 - wip 33253: Fixed THOR-907 "Remove Share functionality which allows access to people profiles outside of your site memberships" part 4 33255: Fixed THOR-907 Remove Share functionality which allows access to people profiles outside of your site memberships part 5 47169: Merged from BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413 35913: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33410: Merged form THOR1_SHARE_PERFORMANCE to THOR1_SPRINTS 33111 Branch for testing out new Spring Surf client side resource improvements 33291 Share client side resource handling to avoid stale client side cache part 1 - New spring surf libs with <@script> & <@link> directives that adds the checksum of the file to avoid client cache beoming stale - Refactored most old element always being printed. 33440: Latest SpringSurf libs - performance and thread safety improvements. 33458: ThorTest-preReg (JMeter) test update 33460: Latest SpringSurf libs: 33466: THOR-1002: Updated enterprise overlay 33480: Latest SpringSurf libs - Surf performance improvements from Thor high load profiling in Jmeter/Jprofiler 35915: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33493: THOR-979: HTML5 upload support 33505: THOR-983: Preload images, JS and CSS for basic dashboards, document library and document details (from login page) 33518: THOR-979: HTML5 upload tweaks (upload doesn't start automatically when updating to give opportunity to set version type and add comment 33520: THOR-900: Modified header.get.html.ftl to ensure that user name is URL encoded (so that the "@" symbol in the user name becomes "%40" to ensure that timeout redirects work) 33527: THOR-1027: Header Alfresco image now links back to application context and about dialog is now linked from footer Alfresco image 33551: THOR-1007: Fixed upload hang on FireFox when uploading folders 35916: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33556: Merged BRANCHES/DEV/THOR1_INVITATION to BRANCHES/DEV/THOR1_SPRINTS: 33386: Branch for Invitation enhancements 33474: THOR-1006. Part 1. Services-level changes to support invitation enhancements. 33475: THOR-1006. Documentation on the desc.xml. 33476: THOR-1006. Commenting out some unfinished code to avoid any unwanted side-effects. Still to do: get the authentication check working and ensure no unexpected side-effects. 33483: THOR-1006. Completion of basic services changes to support 'accept invitation on alternate email'. Note! The authentication of the alternative email's password is NOT YET IMPLEMENTED due to a repo dependency. This MUST be implemented before merge to THOR1_SPRINTS. I'll create a new JIRA. 33511: Fix for THOR-1017. 33525: THOR-1017. Slight improvement to desc.xml doc. HTTP status codes in response. 33529: Fixed THOR-980 & THOR-1024 & THOR-1025 33553: Fixed THOR-980 "F14: Allow users to login using existing email address if invite is sent to wrong email address and they already have an account" 33571: Fixed HTML5 uploader to work with profile avatar image upload 33585: Thor JMeter test script tweaks 33596: THOR-1035: Enabled HTML5 uploader for application logo upload 33598: THOR-1031: Reduced HTML5 checks for uploader to ensure that it works for Safari on Mac 33603: THOR-1039: Updated UX for HTML5 upload when 0kb files are selected 33606: THOR-1037: Updated variable titles for HTML5/DND upload dialog to support update 35917: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33615: Latest SpringSurf libs - performance and concurrency improvements 33690: Resolve THOR-1003: Forgotten Password email is case sensitive 33692: Merged BRANCHES/DEV/THOR1_PRIVACY to BRANCHES/DEV/THOR1_SPRINTS: 33488: Reversed merge revisions related to THOR-907 - 33232, 33233, 33236 - hand tweaks related to 33253, 33255. 33492: Removed unused import of com.sun... class 33497: Fixes and improvements to user profile page loading - reducing remote calls required and refactoring link build code. Fixed a issue with displaying the Following link on other users profile page. 33506: THOR-1020: people visibility 33509: Reduced remote calls required to build user profile page. 33519: THOR-985, THOR-986 33542: THOR-989 - Added extensibility hooks to People Finder component 33558: THOR-1014: Profile visibility -1st cut for THOR-993 (/api/people) 33564: THOR-1014: Profile visibility - THOR-992 (/webframework/content/metadata?user=) 33569: Implemented THOR-985, THOR-986, THOR-989 33572: Performance improvement to remove the need for a share->repo call for each page or ajax request to resolve account class name. 33579: THOR-1020: cloud people API (re: visibility) 33599: Share Thor performance improvements - removed the need to call /internal/cloud/current-user inside various common components - now using cached data in user object. 33608: THOR-1014: Profile visibility - fix PeopleRestApiTest 33625: THOR-1020: people visibility 33632: THOR-984 - Hide Account Settings screen from External Users. 33636: THOR-1014: profile visibility 33670: THOR-1020/THOR-1014: people/profile visibility 33674: THOR-1047: Privacy REST - subscriptions (follower) API 33688: THOR-1047: Privacy REST - subscriptions (follower) API 33696: Fix for THOR-785 "F272: API call to get the number of accounts" 33698: THOR-1033: Fixed free accounts showing console settings (updated Spring Surf libs) 33700: Committed other Surf updates missing from r33698 (WebScript JARs) to ensure that manifest meta-data isn't misleading 33705: THOR-1052: VersionService: ensureVersioningEnabled 33706: Merged BRANCHES/DEV/THOR1_UPLOADLIMITS to BRANCHES/DEV/THOR1_SPRINTS: 33510: (RECORD ONLY) File Upload Limit enhancements 33656: Account Quotas / File Upload limit pt1 33686: Modified dnd-upload and html-upload WebScripts to retrieve maximum upload size from internal service (and refactored core WebScripts to support override) 33710: THOR-1020: Privacy (People REST API) 33713: THOR-1020: Privacy (People REST API) 33718: THOR-1020: Privacy (People REST API) 33722: Thor JMeter test script 33742: Latest SpringSurf libs - performance improvements and concurrency fixes 35930: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33764: THOR-1021: F287: Account Types can define file size upload limits for the Account which are set when the account is upgraded/downgraded between Account types 33767: Tweaked HTML5 upload dialog so that error messages are handled gracefully 33785: Resolve THOR-457: Already have an account email template needs updating/fixing 33786: Fix to issue spotted by DaveC where switching networks would not correctly refresh user metadata. Also fixed minor encoding issues in related Networks WebScripts. 33792: Merged BRANCHES/DEV/THOR1_PUBLIC_EMAIL to BRANCHES/DEV/THOR1_SPRINTS: 33490: Initial feature branch 33535: Fix to unreported issue whereby DirectoryService.getDefaultAccount returns the home account. 33547: THOR-176. Invite new user (public domain email address) into site. Part 1. 33592: Further work for THOR-176. user metadata REST API now does not return homeTenant if the user is from a public email domain. This conditional removal of the 'homeTenant' JSON property is needed by Share. 33593: THOR-176. Refactor of UserTenant to use AccountClass to check isPublicEmailDomain. 33620: Fix for NPE in UserTenant.isPublicDomainUser(). The admin user has no account-type. 33627: Share updates for public e-mail 33797: THOR-176: consolidate public domain check 33802: Remove temporary option to use double @ login (as per THOR-156) - no longer required 33804: Removed locale from the cachekey used for tenant page view cache. 33810: Merged BRANCHES/DEV/THOR1_BLACKLIST to BRANCHES/DEV/THOR1_SPRINTS: 33709: Blacklist CRUD: THOR-974, THOR-975, THOR-976, THOR-977, THOR-978 (Part 1 - DAO layer) 33711: Blacklist CRUD: THOR-974, THOR-975, THOR-976, THOR-977, THOR-978 (Part 2 - Foundation Service layer) 33747: THOR-974, THOR-975, THOR-976, THOR-977 and THOR-978. REST API for CRUD of blacklisted email domains. 33809: THOR-974, THOR-975, THOR-976, THOR-977, THOR-978 Adding REST-client rcq files for blacklist CRUD. 35933: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33814: Merged BRANCHES/DEV/THOR1_ACCOUNT_SETTINGS to BRANCHES/DEV/THOR1_SPRINTS: 33411: (RECORD ONLY) Thor account settings branch from Thor1_Sprints. 33607: Initial checkin for THOR-972, THOR-971, THOR-410 33621: THOR-972, THOR-971, THOR-410 - added missing files 33639: THOR-971, THOR-972, THOR-410: - add paging properties to the list people web script - default network admin to true and internal to null 33641: Second cut of THOR-964 "F173: Network admin can list users of network (with paging support)" 33642: THOR-972, THOR-971, THOR-410 - changed paging properties in list users 33652: Implemented THOR-964,THOR-965,THOR-965,THOR-966,THOR-967,THOR-968,THOR-969 33653: User action click event now stopped so it doesn't modify the url 33671: THOR-971: webscript implementation 33687: Making premote/demote available for network-admins and not only for admins. check for network admin role already exists in the service code. 33694: THOR-971: - Added analytics handling - Fixed invite share url to include tenant 33699: THOR-963 "F17: Network admin can add a one or more internal users to their network" 33702: THOR-971: - changed the bulk create url - changed the activate email template 33703: Make sure Java-based network admin scripts set the response status to 401 if the authenticated user is not a network admin 33737: THOR-410: - more unit tests - tidy up 33744: (RECORD ONLY) Merged BRANCHES/DEV/THOR1_USER_MANAGEMENT to BRANCHES/DEV/THOR1_ACCOUNT_SETTINGS: 33417: Thor JMeter test script tweaks 33420: THOR-1000: Solr tracking: NodeContentGet should not create (empty) temp file if there is no transformer (eg. for image node) 33434: The <#if> statement & element always being printed. 33440: Latest SpringSurf libs - performance and thread safety improvements. 33458: ThorTest-preReg (JMeter) test update 33460: Latest SpringSurf libs: 33466: THOR-1002: Updated enterprise overlay 33480: Latest SpringSurf libs - Surf performance improvements from Thor high load profiling in Jmeter/Jprofiler 33493: THOR-979: HTML5 upload support 33505: THOR-983: Preload images, JS and CSS for basic dashboards, document library and document details (from login page) 33518: THOR-979: HTML5 upload tweaks (upload doesn't start automatically when updating to give opportunity to set version type and add comment 33520: THOR-900: Modified header.get.html.ftl to ensure that user name is URL encoded (so that the "@" symbol in the user name becomes "%40" to ensure that timeout redirects work) 33527: THOR-1027: Header Alfresco image now links back to application context and about dialog is now linked from footer Alfresco image 33551: THOR-1007: Fixed upload hang on FireFox when uploading folders 33556: Merged BRANCHES/DEV/THOR1_INVITATION to BRANCHES/DEV/THOR1_SPRINTS: 33386: Branch for Invitation enhancements 33474: THOR-1006. Part 1. Services-level changes to support invitation enhancements. 33475: THOR-1006. Documentation on the desc.xml. 33476: THOR-1006. Commenting out some unfinished code to avoid any unwanted side-effects. Still to do: get the authentication check working and ensure no unexpected side-effects. 33483: THOR-1006. Completion of basic services changes to support 'accept invitation on alternate email'. Note! The authentication of the alternative email's password is NOT YET IMPLEMENTED due to a repo dependency. This MUST be implemented before merge to THOR1_SPRINTS. I'll create a new JIRA. 33511: Fix for THOR-1017. 33525: THOR-1017. Slight improvement to desc.xml doc. HTTP status codes in response. 33529: Fixed THOR-980 & THOR-1024 & THOR-1025 33553: Fixed THOR-980 "F14: Allow users to login using existing email address if invite is sent to wrong email address and they already have an account" 33559: Initial feature branch 33669: First cut of THOR-994 and THOR-995 - remove internal and external user from network. 33685: Making remove-external-user.delete.desc.xml accept a domainName templateArg as well as the existing accountId. 33716: THOR-994 and THOR-995. Remove user from network. Addressing some review comments from DaveC. Refactoring mostly. Also added protection on RegistrationService.deleteUser() to prevent deletion of last NetworkAdmin in network. 33745: Adding NetworkdAdmin protection to the remove-external-user.delete webscript. 33752: Additional fixes for THOR-966 & THOR-969 * Improved messages/dialogs: demoting yourself, demoting last admin, removing last admin * New User Button align layout fix as requested by Imran 33754: Documentation for the remove-external-user.delete webscript. 33756: THOR-410: - unit test tidy 33766: Fix for signup link when already logged in as another user 33769: THOR-963 "F17: Network admin can add a one or more internal users to their network" 33770: Ensure a 403 is returned (rather than 500) when attempt is made to remove last NetworkAdmin in a tenant. 33774: Added padding on top of name for the Manage Users screen as requested by ux 33790: Promote/demote icons from Imran 33815: Fix issue with removal of public email user from last invited network 33817: THOR-1060: Activities Feed - perf tweak to halve the number of generated feed entries 33819: THOR-1060: fix ActivitiesFeed subsystem (re-)name 33820: Merged BRANCHES/DEV/THOR1_ACCOUNT_SETTINGS to BRANCHES/DEV/THOR1_SPRINTS: 33756: THOR-410: - unit test tidy 33766: Fix for signup link when already logged in as another user 33769: THOR-963 "F17: Network admin can add a one or more internal users to their network" 33770: Ensure a 403 is returned (rather than 500) when attempt is made to remove last NetworkAdmin in a tenant. 33774: Added padding on top of name for the Manage Users screen as requested by ux 33790: Promote/demote icons from Imran 33825: People REST API 35934: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 33850: (RECORD ONLY) Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS 33843: Fix for ALF-12775 33866: Fix for THOR-1071 33878: Fix the build 33881: THOR-1069: Ensure that invitations can be accepted when a user is already logged in 33882: Resolve THOR-1082: Possible to register email address with invalid domain (according to our tenant id rules) 33883: Resolve THOR-1070: External user's avatar not displayed on the People Finder page. 33884: Fix solrcore.properties 33899: Resolve THOR-1077: Incorrect free space displayed when uploading files which exceeds quota 33922: Resolve THOR-1079: Incorrect behavior of the button "Save and close" to "Send Document (s) For Review" tasks. 33933: Resolve THOR-1088: Hide Account Id from Account Summary Screen 33934: Resolve THOR-1089: Review Account Quota text on Account Summary Screen 33942: Fix for THOR-1094. InvalidDomains FTL couldn't handle NULL notes field. This shouldn't arise in the field as we don't put NULL-valued notes in the DB, but it might matter in some test envs. 33949: Resolve THOR-1093: Incorrect notification title displayed when trying to invite user from another network from Manage Users page 33953: Fix for THOR issue where public users should not be able to see Following and Following Me tabs in their own profile. 35954: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 34140: THOR-1098: Prevent resources being requested twice (latest Surf libs) 34153: Minor: THOR-5: MT-aware immutable singletons (spp/vti) 34161: Prevent session timeout redirect problem resulting from clicking user link in activities feed 35960: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 34224: Fix for THOR-789 - Mix of languages. The original bug was not never actually completely fixed, added some additional handling in SpringSurf WebScripts View to not override the locale from the original request parameters if it has already been set elsewhere. 34301: (RECORD ONLY) Merged BRANCHES/DEV/V4.0-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS: 34279: NodeDAO: re-parent "lost & found" orphan child nodes (see ALF-12358 & ALF-13066 / SYS-301) 34343: (RECORD ONLY) Merged BRANCHES/DEV/V4.0-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS: 34338: NodeDAO: re-parent "lost & found" orphan child nodes (see ALF-12358 & ALF-13066 / SYS-301) - test fix 34341: NodeDAO: re-parent "lost & found" orphan child nodes (see ALF-12358 & ALF-13066 / SYS-301) - test fix 34388: THOR-953/SYS-294: add db.pool.evict.num.tests option (=> numTestsPerEvictionRun) 34729: (RECORD ONLY) Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS: 31867: Merged DEV/TEMPORARY to V3.4-BUG-FIX 31400: ALF-10764: PDF vs 1.5 cause crash jvm - PDFRenderer library has been updated from 2009-09-27 to 0.9.1 version to support PDF documents of 1.5 version 32061: ALF-11376 Requesting PDFBox 1.6 be included in future service pack release. Upgrading pdfbox,fontbox,jempbox from 1.5.0 to 1.6.0 34731: THOR-1261: repo cluster fix (propertyUniqueContextCache) 34734: THOR-1261: repo cluster fix (propertyUniqueContextCache) 34435: Merged BRANCHES/DEV/V4.0-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS: 34434: ALF-13066: Fix for intermittent failure (testConcurrentLinkToDeletedNode) 35961: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 34558: THOR-1216: tenant context mismatch (Solr tracking) 34606: THOR-1216: tenant context mismatch 34441: (RECORD ONLY) Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/THOR1_SPRINTS 33285: Fix for ALF-12336 - Share loses performance if noncachableObjectTypes are defined (page & component) 34489: Fix to remove hazelcast subdir from build.xml for -exploded build - Thor specific merge issue. 34722: Added hazelcast-cloud jar to allow AWS Hazelcast config options for Share clustering on Thor 34848: THOR - specific version of ClusterAwarePathStoreObjectPersister. 34931: Thor specific lookup of Share custom app context files to include the custom-slingshot-cloud-context and custom-slingshot-application-context only and in the order we want. Also updated Hazelcast example config to include AWS by default 35962: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 34940: THOR-1288: Extra diagnostics for tracking leaked tenant context on thread: 34187: Updated Surf libs (down grades duplicate dependency warnings to debug info) 34410: THOR-1169: Latest Spring Surf libs to fix missing template CSS probs 34418: (RECORD ONLY) Merged BRANCHES/DEV/BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/BRANCHES/DEV/THOR1_SPRINTS 34316: Method signature change to ConfigService fixes for RepoXMLConfigService 34471: (RECORD ONLY) Merged BRANCHES/V4.0 to BRANCHES/DEV/THOR1_SPRINTS 34468: Fix for ALF-13172 Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/V4.0 34467: Fix for ALF-13237 - Change dashboard Layout is not working correctly, original layout is still used after saving changes. 34891: Added missing jug-asl-2.0.0.jar to slingshot deps for Thor 35963: Merged BRANCHES/DEV/THOR1_SPRINTS to BRANCHES/DEV/CLOUD1: 35087: Minor: remove NOOP (introduced in r30776) 35123: THOR-1288: update leak logger 35124: THOR-1288: prod login failure when using cloud console for (bulk) signups 35132: THOR-1288: build/test fix 35133: THOR-1288: build/test fix 35395: Resolve THOR-1340: Alberto.Vazquez@w.illi.am cannot sign up 35964: Spring Surf library refresh 35995: Fix merge issue 35999: Fix merge issue 36053: 1st pass at upgrading to latest Spring Surf 36059: Fix CloudInvitationService tests for cloud1 48191: Merged BRANCHES/DEV/CONV_V413 to BRANCHES/DEV/CONV_HEAD: 47185: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: Merged BRANCHES/DEV/CLOUD2 to BRANCHES/DEV/CONV_V413: MT - enable ability to get call context if overriding of beginCall/afterCall - eg. for cloud use-case (x-network switching) 48192: Temporarily disable generation of installers, to speed up build git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@48255 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/action-services-context.xml | 65 ++--- .../authentication-services-context.xml | 15 +- .../alfresco/bootstrap/alfrescoUserStore.xml | 2 + .../bootstrap/categoriesEmptyRoot.xml | 15 - .../bootstrap/webScriptsNoSamples.xml | 34 --- config/alfresco/cache-context.xml | 16 +- config/alfresco/content-services-context.xml | 2 +- config/alfresco/core-services-context.xml | 3 + config/alfresco/dao/dao-context.xml | 4 + config/alfresco/import-export-context.xml | 10 +- .../messages/subscription-service.properties | 2 +- .../public-services-security-context.xml | 4 +- config/alfresco/repository.properties | 24 +- .../default/activities-feed-context.xml | 6 +- .../default/activities-jobs.properties | 3 + .../alfresco-authentication-context.xml | 1 + .../default/subscription-service-context.xml | 11 + .../default/subscription-service.properties | 5 +- .../OutboundSMTP/outboundSMTP-context.xml | 10 + .../OutboundSMTP/outboundSMTP.properties | 8 + .../activities-email.ftl | 2 +- .../activities-email_de.ftl | 2 +- .../activities-email_es.ftl | 2 +- .../activities-email_fr.ftl | 2 +- .../activities-email_it.ftl | 2 +- .../activities-email_ja.ftl | 2 +- .../activities-email_nl.ftl | 2 +- config/alfresco/thumbnail-service-context.xml | 5 +- config/alfresco/tx-cache-context.xml | 30 +- config/alfresco/workflow/adhoc.bpmn20.xml | 2 +- .../workflow/workflow-messages.properties | 52 +++- pom.xml | 4 +- .../CMISAbstractDictionaryService.java | 7 +- .../opencmis/AlfrescoCmisServiceFactory.java | 7 +- .../opencmis/AlfrescoCmisServiceImpl.java | 49 ++-- .../org/alfresco/repo/action/ActionImpl.java | 14 +- .../repo/action/ActionServiceImpl.java | 29 +- .../AsynchronousActionExecutionQueueImpl.java | 38 ++- .../action/executer/MailActionExecuter.java | 53 +++- .../repo/activities/ActivityServiceImpl.java | 21 +- .../activities/feed/AbstractUserNotifier.java | 4 +- .../activities/feed/EmailUserNotifier.java | 20 +- .../feed/ErrorProneUserNotifier.java | 3 +- .../activities/feed/FeedNotifierImpl.java | 48 +++- .../activities/feed/MockUserNotifier.java | 20 +- .../repo/activities/feed/UserNotifier.java | 20 +- .../activities/feed/cleanup/FeedCleaner.java | 25 +- .../feed/local/LocalFeedTaskProcessor.java | 15 +- ...ateWorkflowNotificationTemplatesPatch.java | 4 +- .../repo/attributes/AttributeServiceImpl.java | 8 +- .../repo/audit/AuditComponentTest.java | 2 - .../repo/config/xml/RepoXMLConfigService.java | 7 +- .../repo/dictionary/RepositoryLocation.java | 12 +- .../domain/avm/ibatis/AVMLockDAOImpl.java | 56 ++-- .../repo/domain/node/AbstractNodeDAOImpl.java | 14 +- .../alfresco/repo/domain/node/NodeDAO.java | 2 +- .../propval/AbstractPropertyValueDAOImpl.java | 266 +++++++++++++++--- .../repo/domain/propval/PropertyValueDAO.java | 8 +- .../domain/propval/PropertyValueDAOTest.java | 8 +- .../org/alfresco/repo/jscript/People.java | 210 +++++++++----- .../repo/model/filefolder/HiddenAspect.java | 75 +++-- .../repo/module/ModuleComponentHelper.java | 79 +++--- .../repo/module/ModuleServiceImpl.java | 4 + .../EMailNotificationProvider.java | 14 +- .../NotificationServiceImplSystemTest.java | 4 +- .../repo/rendition/RenditionServiceImpl.java | 6 +- .../RenditionServiceIntegrationTest.java | 3 +- .../AuthenticationServiceImpl.java | 17 +- .../authentication/AuthenticationTest.java | 5 + .../MessageDigestPasswordEncoder.java | 171 +++++++++++ .../RepositoryAuthenticationDao.java | 117 +++++--- .../ShaPasswordEncoderImpl.java | 67 +++++ .../security/authentication/userModel.xml | 3 + .../script/ScriptAuthorityService.java | 2 + .../person/GetPeopleCannedQueryFactory.java | 3 +- .../security/person/PersonServiceImpl.java | 84 +++--- .../ChainingUserRegistrySynchronizerTest.java | 20 +- .../repo/site/SitesPermissionCleaner.java | 2 +- .../SubscriptionServiceImpl.java | 89 ++++-- .../AbstractTenantRoutingContentStore.java | 19 +- .../repo/tenant/MultiTAdminServiceImpl.java | 221 ++++++++------- .../alfresco/repo/tenant/MultiTDemoTest.java | 2 +- .../repo/tenant/MultiTServiceImpl.java | 27 +- .../repo/tenant/RunAsTenantInterceptor.java | 3 +- .../repo/tenant/TenantBasicDataSource.java | 25 +- .../tenant/TenantRoutingFileContentStore.java | 1 - .../repo/thumbnail/ThumbnailRegistry.java | 107 ++++--- .../repo/version/VersionServiceImpl.java | 2 +- .../workflow/WorkflowNotificationUtils.java | 15 +- .../cmr/notification/NotificationContext.java | 12 +- .../service/cmr/security/PersonService.java | 12 +- 91 files changed, 1806 insertions(+), 722 deletions(-) delete mode 100644 config/alfresco/bootstrap/categoriesEmptyRoot.xml delete mode 100644 config/alfresco/bootstrap/webScriptsNoSamples.xml create mode 100644 source/java/org/alfresco/repo/security/authentication/MessageDigestPasswordEncoder.java create mode 100644 source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 053bf1a317..9f750e8a40 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -51,30 +51,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -98,20 +74,37 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 9a2dd30f0b..e4069ddc88 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -154,8 +154,11 @@ - + + + + 256 + @@ -293,10 +296,10 @@ - - - ${home.folder.creation.eager} - + + + + diff --git a/config/alfresco/bootstrap/alfrescoUserStore.xml b/config/alfresco/bootstrap/alfrescoUserStore.xml index a1e187c835..93a21a651b 100644 --- a/config/alfresco/bootstrap/alfrescoUserStore.xml +++ b/config/alfresco/bootstrap/alfrescoUserStore.xml @@ -11,6 +11,8 @@ ${alfresco_user_store.adminusername} ${alfresco_user_store.adminpassword} + ${alfresco_user_store.adminsalt} + ${alfresco_user_store.adminpassword2} true false false diff --git a/config/alfresco/bootstrap/categoriesEmptyRoot.xml b/config/alfresco/bootstrap/categoriesEmptyRoot.xml deleted file mode 100644 index b7bbabcf28..0000000000 --- a/config/alfresco/bootstrap/categoriesEmptyRoot.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - categories - - - General - - - - - - \ No newline at end of file diff --git a/config/alfresco/bootstrap/webScriptsNoSamples.xml b/config/alfresco/bootstrap/webScriptsNoSamples.xml deleted file mode 100644 index f9550b2d75..0000000000 --- a/config/alfresco/bootstrap/webScriptsNoSamples.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - ${webscripts.url_addressable_web_services} - space-icon-default - ${webscripts.url_addressable_web_services} - ${webscripts.web_scripts} - - - - - - - - - - - true - ${webscripts.what_are_web_scripts} - contentUrl=classpath:alfresco/bootstrap/webscripts/readme.html|mimetype=text/html|size=|encoding=UTF-8|locale=en_US_ - - - readme.html - - - - - - - \ No newline at end of file diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 25d03b122a..ea4eca3048 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -13,11 +13,17 @@ - - + + + + + + + + + + + diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index e75c214c24..92ea231226 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -13,7 +13,7 @@ --> - + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 9c0da6bbcb..fcc02a3266 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -286,6 +286,9 @@ ${db.pool.evict.idle.min} + + ${db.pool.evict.num.tests} + ${db.pool.validate.borrow} diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index 881888fdc7..b3cc499ac4 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -173,6 +173,9 @@ --> + + + @@ -213,6 +216,7 @@ + diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index 9aa2f29eb8..df5a8a0c0b 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -306,6 +306,8 @@ ${alfresco_user_store.adminusername} ${alfresco_user_store.adminpassword} + ${alfresco_user_store.adminsalt} + ${alfresco_user_store.adminpassword2} ${alfresco_user_store.system_container.childname} ${alfresco_user_store.user_container.childname} @@ -538,10 +540,10 @@ alfresco/messages/bootstrap-spaces - /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname} - alfresco/bootstrap/notification/workflow-email-notification.xml - alfresco/messages/bootstrap-spaces - + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname} + alfresco/bootstrap/notification/workflow-email-notification.xml + alfresco/messages/bootstrap-spaces + /${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.rss.childname} alfresco/templates/rss_templates.acp diff --git a/config/alfresco/messages/subscription-service.properties b/config/alfresco/messages/subscription-service.properties index fa480dc1ed..9aef32cab9 100644 --- a/config/alfresco/messages/subscription-service.properties +++ b/config/alfresco/messages/subscription-service.properties @@ -1,6 +1,6 @@ # Subscription service messages -subscription.notification.email.subject={0} is now following you +subscription.notification.email.subject={0} is now following you on Alfresco subscription_service.err.disabled=The subscription is disabled subscription_service.err.write-denied=No permissions to update diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index c3abd3c9d3..8ae53c972d 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -874,7 +874,7 @@ org.alfresco.service.cmr.security.PersonService.deletePerson=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.PersonService.notifyPerson=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.PersonService.getAllPeople=ACL_ALLOW - org.alfresco.service.cmr.security.PersonService.getPeople=ACL_ALLOW + org.alfresco.service.cmr.security.PersonService.getPeople=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties org.alfresco.service.cmr.security.PersonService.getPeopleFilteredByProperty=ACL_ALLOW org.alfresco.service.cmr.security.PersonService.getPeopleContainer=ACL_ALLOW org.alfresco.service.cmr.security.PersonService.getUserNamesAreCaseSensitive=ACL_ALLOW @@ -886,7 +886,7 @@ - + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 5d1be6df72..4c571b0117 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -340,13 +340,25 @@ db.pool.statements.max=40 db.pool.min=0 db.pool.idle=-1 db.pool.wait.max=-1 + db.pool.validate.query= db.pool.evict.interval=-1 db.pool.evict.idle.min=1800000 +# +# note: for 'db.pool.evict.num.tests' see http://commons.apache.org/dbcp/configuration.html (numTestsPerEvictionRun) +# and also following extract from "org.apache.commons.pool.impl.GenericKeyedObjectPool" (1.5.5) +# +# * The number of objects to examine during each run of the idle object evictor thread (if any). +# * When a negative value is supplied, ceil({@link #getNumIdle})/abs({@link #getNumTestsPerEvictionRun}) +# * tests will be run. I.e., when the value is -n, roughly one nth of the +# * idle objects will be tested per run. +# +db.pool.evict.num.tests=-1 + +db.pool.evict.validate=false db.pool.validate.borrow=true db.pool.validate.return=false -db.pool.evict.validate=false -# + db.pool.abandoned.detect=false db.pool.abandoned.time=300 # @@ -387,6 +399,8 @@ alfresco_user_store.adminusername=admin # Initial password - editing this will not have any effect once the repository is installed alfresco_user_store.adminpassword=209c6174da490caeb422f3fa5a7ae634 +alfresco_user_store.adminsalt=ad3b938f-c1ad-4f2b-828b-6f3afd30ffdd +alfresco_user_store.adminpassword2=f378d5d7b947d5c26f478e21819e7ec3a6668c8149b050d086c64447bc40173b # note: default guest username - should not be changed after installation alfresco_user_store.guestusername=guest @@ -520,8 +534,10 @@ repo.remote.endpoint=/service # persisted. create.missing.people=${server.transaction.allow-writes} -# Create home folders as people are created (true) or create them lazily (false) +# Create home folders (unless disabled, see next property) as people are created (true) or create them lazily (false) home.folder.creation.eager=true +# Disable home folder creation - if true then home folders are not created (neither eagerly nor lazily) +home.folder.creation.disabled=false # Should we consider zero byte content to be the same as no content when firing # content update policies? Prevents 'premature' firing of inbound content rules @@ -618,6 +634,7 @@ system.thumbnail.retryPeriod=60 system.thumbnail.retryCount=2 system.thumbnail.quietPeriod=604800 system.thumbnail.quietPeriodRetriesEnabled=true +system.thumbnail.redeployStaticDefsOnStartup=true # Content Transformers content.transformer.failover=true @@ -944,6 +961,7 @@ system.quickshare.enabled=true # Cache configuration # cache.propertyValueCache.maxItems=10000 +cache.propertyUniqueContextSharedCache.maxItems=10000 cache.contentDataSharedCache.maxItems=130000 cache.immutableEntitySharedCache.maxItems=50000 cache.node.rootNodesSharedCache.maxItems=1000 diff --git a/config/alfresco/subsystems/ActivitiesFeed/default/activities-feed-context.xml b/config/alfresco/subsystems/ActivitiesFeed/default/activities-feed-context.xml index 76ebd87449..672d4bd19d 100644 --- a/config/alfresco/subsystems/ActivitiesFeed/default/activities-feed-context.xml +++ b/config/alfresco/subsystems/ActivitiesFeed/default/activities-feed-context.xml @@ -29,6 +29,7 @@ + @@ -145,7 +146,10 @@ - /app:company_home/app:dictionary/app:email_templates/cm:activities/cm:activities-email.ftl + ${activities.feed.notifier.emailTemplatePath} + + + ${activities.feed.notifier.emailTemplateLocationType} diff --git a/config/alfresco/subsystems/ActivitiesFeed/default/activities-jobs.properties b/config/alfresco/subsystems/ActivitiesFeed/default/activities-jobs.properties index f34f85713b..b57ea9836b 100644 --- a/config/alfresco/subsystems/ActivitiesFeed/default/activities-jobs.properties +++ b/config/alfresco/subsystems/ActivitiesFeed/default/activities-jobs.properties @@ -13,6 +13,9 @@ activities.feed.notifier.startDelayMins=0 activities.feed.notifier.repeatIntervalMins=1440 activities.feed.notifier.enabled=true +activities.feed.notifier.emailTemplatePath=/app:company_home/app:dictionary/app:email_templates/cm:activities/cm:activities-email.ftl +activities.feed.notifier.emailTemplateLocationType=xpath + # activities feed generator activities.feed.generator.startDelayMins=0 activities.feed.generator.repeatIntervalMillis=30000 diff --git a/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml b/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml index 5339358b4d..7fd770e7af 100644 --- a/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml +++ b/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml @@ -82,6 +82,7 @@ + diff --git a/config/alfresco/subsystems/Subscriptions/default/subscription-service-context.xml b/config/alfresco/subsystems/Subscriptions/default/subscription-service-context.xml index eb48196228..aa651736c6 100644 --- a/config/alfresco/subsystems/Subscriptions/default/subscription-service-context.xml +++ b/config/alfresco/subsystems/Subscriptions/default/subscription-service-context.xml @@ -19,6 +19,16 @@ + + + + ${subscriptions.following.emailTemplatePath} + + + ${subscriptions.following.emailTemplateLocationType} + + + @@ -31,6 +41,7 @@ + \ No newline at end of file diff --git a/config/alfresco/subsystems/Subscriptions/default/subscription-service.properties b/config/alfresco/subsystems/Subscriptions/default/subscription-service.properties index edcf341a5d..ac6d462089 100644 --- a/config/alfresco/subsystems/Subscriptions/default/subscription-service.properties +++ b/config/alfresco/subsystems/Subscriptions/default/subscription-service.properties @@ -1,2 +1,5 @@ # Enables the subscription service -subscriptions.enabled=true \ No newline at end of file +subscriptions.enabled=true + +subscriptions.following.emailTemplatePath=/app:company_home/app:dictionary/app:email_templates/app:following/cm:following-email.html.ftl +subscriptions.following.emailTemplateLocationType=xpath \ No newline at end of file diff --git a/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP-context.xml b/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP-context.xml index 782062d059..e6faa1b2f8 100755 --- a/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP-context.xml +++ b/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP-context.xml @@ -67,6 +67,9 @@ ${mail.header} + + ${mail.validate.addresses} + ${mail.from.default} @@ -88,6 +91,13 @@ ${mail.testmessage.text} + + + + + + + diff --git a/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP.properties b/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP.properties index 5ef2954780..a342659f4d 100755 --- a/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP.properties +++ b/config/alfresco/subsystems/email/OutboundSMTP/outboundSMTP.properties @@ -34,3 +34,11 @@ mail.testmessage.send=false mail.testmessage.to= mail.testmessage.subject=Outbound SMTP mail.testmessage.text=The Outbound SMTP email subsystem is working. + +# validate email addresses +mail.validate.addresses= true + +# NOTE: DO NOT remove this value - leave the value empty if you don't want to override +dev.email.recipient.address= +# NOTE: DO NOT remove this value - leave as false if you don't want to override (if true then emails will not be sent) +dev.email.not.sent=false \ No newline at end of file diff --git a/config/alfresco/templates/activities-email-templates/activities-email.ftl b/config/alfresco/templates/activities-email-templates/activities-email.ftl index 032103a54f..6830eddb7c 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_de.ftl b/config/alfresco/templates/activities-email-templates/activities-email_de.ftl index bd39a19750..549a4b0bda 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_de.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_de.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_es.ftl b/config/alfresco/templates/activities-email-templates/activities-email_es.ftl index 3fc1e3ce7f..a8ec544a3d 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_es.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_es.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_fr.ftl b/config/alfresco/templates/activities-email-templates/activities-email_fr.ftl index c68d902592..c8ee76548c 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_fr.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_fr.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_it.ftl b/config/alfresco/templates/activities-email-templates/activities-email_it.ftl index f2a56632aa..da5cd6647f 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_it.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_it.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_ja.ftl b/config/alfresco/templates/activities-email-templates/activities-email_ja.ftl index 4c146a70a2..d45475e824 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_ja.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_ja.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/templates/activities-email-templates/activities-email_nl.ftl b/config/alfresco/templates/activities-email-templates/activities-email_nl.ftl index b9bebaea66..f0403a4075 100644 --- a/config/alfresco/templates/activities-email-templates/activities-email_nl.ftl +++ b/config/alfresco/templates/activities-email-templates/activities-email_nl.ftl @@ -120,7 +120,7 @@ - + diff --git a/config/alfresco/thumbnail-service-context.xml b/config/alfresco/thumbnail-service-context.xml index 529b02cde5..222509d785 100644 --- a/config/alfresco/thumbnail-service-context.xml +++ b/config/alfresco/thumbnail-service-context.xml @@ -202,11 +202,14 @@ - + + + + diff --git a/config/alfresco/tx-cache-context.xml b/config/alfresco/tx-cache-context.xml index 1b3e3bedf9..9503f268d8 100644 --- a/config/alfresco/tx-cache-context.xml +++ b/config/alfresco/tx-cache-context.xml @@ -1,11 +1,10 @@ - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> + - + @@ -16,10 +15,25 @@ + - - - + + + + + + + + + org.alfresco.cache.propertyUniqueContextTransactionalCache + + + + + + + + diff --git a/config/alfresco/workflow/adhoc.bpmn20.xml b/config/alfresco/workflow/adhoc.bpmn20.xml index 3c385f4d1c..9697da3526 100644 --- a/config/alfresco/workflow/adhoc.bpmn20.xml +++ b/config/alfresco/workflow/adhoc.bpmn20.xml @@ -40,7 +40,7 @@ - Verify the arbitrary task was completed. + Verify the task was completed. diff --git a/config/alfresco/workflow/workflow-messages.properties b/config/alfresco/workflow/workflow-messages.properties index 64459d39cf..fdcc13f648 100644 --- a/config/alfresco/workflow/workflow-messages.properties +++ b/config/alfresco/workflow/workflow-messages.properties @@ -90,8 +90,8 @@ wf_parallelgroupreview.node.review.transition.approve.description=Approve # Activiti Adhoc Task Workflow # -activitiAdhoc.workflow.title=Adhoc Workflow -activitiAdhoc.workflow.description=Assign arbitrary task to colleague using Activiti workflow engine +activitiAdhoc.workflow.title=New Task +activitiAdhoc.workflow.description=Assign a new task to yourself or a colleague # # Activiti Review And Approve Workflow @@ -105,8 +105,8 @@ activitiReview.task.rejected.description=The document was reviewed and rejected. # Parallel Review Workflow # -activitiParallelReview.workflow.title=Parallel Review And Approve -activitiParallelReview.workflow.description=Parallel Review and approval of content using Activiti workflow engine +activitiParallelReview.workflow.title=Send Document(s) For Review +activitiParallelReview.workflow.description=Request document approval from one or more colleagues activitiParallelReview.task.approved.description=The document was reviewed and approved. activitiParallelReview.task.rejected.description=The document was reviewed and rejected. @@ -141,14 +141,23 @@ publishWebContent.workflow.description=Publishing of web content using Activiti # Adhoc Task Definitions -wf_workflowmodel.type.wf_submitAdhocTask.title=Start Adhoc Task +wf_workflowmodel.type.wf_submitAdhocTask.title=Task wf_workflowmodel.type.wf_submitAdhocTask.description=Allocate task to colleague wf_workflowmodel.property.wf_notifyMe.title=Notify Me wf_workflowmodel.property.wf_notifyMe.description=Notify me when task is complete -wf_workflowmodel.type.wf_adhocTask.title=Adhoc Task -wf_workflowmodel.type.wf_adhocTask.description=Adhoc Task allocated by colleague -wf_workflowmodel.type.wf_completedAdhocTask.title=Adhoc Task Completed -wf_workflowmodel.type.wf_completedAdhocTask.description=Adhoc Task Completed +wf_workflowmodel.type.wf_adhocTask.title=Task +wf_workflowmodel.type.wf_adhocTask.description=Task allocated by colleague +wf_workflowmodel.type.wf_completedAdhocTask.title=Task Completed +wf_workflowmodel.type.wf_completedAdhocTask.description=Task Completed + +activitiAdhoc.task.wf_submitAdhocTask.title=Task +activitiAdhoc.task.wf_submitAdhocTask.description=Allocate task to colleague +activitiAdhoc.property.wf_notifyMe.title=Notify Me +activitiAdhoc.property.wf_notifyMe.description=Notify me when task is complete +activitiAdhoc.task.wf_adhocTask.title=Task +activitiAdhoc.task.wf_adhocTask.description=Task allocated by colleague +activitiAdhoc.task.wf_completedAdhocTask.title=Task Completed +activitiAdhoc.task.wf_completedAdhocTask.description=Task Completed # Review And Approve Task Definitions @@ -169,8 +178,8 @@ wf_workflowmodel.property.wf_reviewOutcome.description=Approve or Reject the con # Parallel Review And Approve Task Definitions -wf_workflowmodel.type.wf_submitParallelReviewTask.title=Start Parallel Review -wf_workflowmodel.type.wf_submitParallelReviewTask.description=Submit documents for review and approval to a list of people +wf_workflowmodel.type.wf_submitParallelReviewTask.title=Send Document(s) For Review +wf_workflowmodel.type.wf_submitParallelReviewTask.description=Request document approval from one or more colleagues wf_workflowmodel.property.wf_requiredApprovePercent.title=Required Approval Percentage wf_workflowmodel.property.wf_requiredApprovePercent.description=Percentage of reviewers who must approve for approval wf_workflowmodel.type.wf_rejectedParallelTask.title=Rejected @@ -188,6 +197,27 @@ wf_workflowmodel.property.wf_actualPercent.description=Actual approval percentag wf_workflowmodel.property.wf_reviewOutcome.title=Review Outcome wf_workflowmodel.property.wf_reviewOutcome.description=Review Outcome +activitiParallelReview.task.wf_submitParallelReviewTask.title=Send Document(s) For Review +activitiParallelReview.task.wf_submitParallelReviewTask.description=Request document approval from one or more colleagues +activitiParallelReview.property.wf_requiredApprovePercent.title=Required Approval Percentage +activitiParallelReview.property.wf_requiredApprovePercent.description=Percentage of reviewers who must approve for approval +activitiParallelReview.task.wf_activitiReviewTask.title=Review +activitiParallelReview.task.wf_activitiReviewTask.description=Review Documents to Approve or Reject them +activitiParallelReview.task.wf_rejectedParallelTask.title=Document Rejected +activitiParallelReview.task.wf_rejectedParallelTask.description=Document(s) were rejected +activitiParallelReview.task.wf_approvedParallelTask.title=Document Approved +activitiParallelReview.task.wf_approvedParallelTask.description=Document(s) were approved +activitiParallelReview.property.wf_reviewerCount.title=Number of Reviewers +activitiParallelReview.property.wf_reviewerCount.description=Number of reviewers +activitiParallelReview.property.wf_requiredPercent.title=Required Approval Percentage +activitiParallelReview.property.wf_requiredPercent.description=Required Approval Percentage +activitiParallelReview.property.wf_approveCount.title=Reviewers Who Approved +activitiParallelReview.property.wf_approveCount.description=Reviewers who approved +activitiParallelReview.property.wf_actualPercent.title=Actual Approval Percentage +activitiParallelReview.property.wf_actualPercent.description=Actual approval percentage +activitiParallelReview.property.wf_reviewOutcome.title=Review Outcome +activitiParallelReview.property.wf_reviewOutcome.description=Review Outcome + # Pooled Review Task Definitions wf_workflowmodel.type.wf_submitGroupReviewTask.title=Start Group Review diff --git a/pom.xml b/pom.xml index 4244511a71..5130f64444 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ alfresco-parent org.alfresco - 4.2.d-SNAPSHOT + HEAD-CONV-SNAPSHOT ../../pom-experimental.xml @@ -559,7 +559,7 @@ edu.ucar netcdf - 4.2 + 4.2-min diff --git a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java index 24bd8be62d..84b9016091 100644 --- a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java +++ b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java @@ -214,7 +214,12 @@ public abstract class CMISAbstractDictionaryService extends AbstractLifecycleBea } } - private DictionaryRegistry getRegistry() + protected DictionaryRegistry getRegistry() + { + return getRegistryImpl(); + } + + protected DictionaryRegistry getRegistryImpl() { DictionaryRegistry registry = null; diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java index c31daa7f44..4babcda8db 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java @@ -129,7 +129,7 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory AuthenticationUtil.clearCurrentSecurityContext(); } - AlfrescoCmisService cmisServiceTarget = new AlfrescoCmisServiceImpl(connector); + AlfrescoCmisService cmisServiceTarget = getCmisServiceTarget(connector); // Wrap it ProxyFactory proxyFactory = new ProxyFactory(cmisServiceTarget); @@ -150,4 +150,9 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory return wrapperService; } + + protected AlfrescoCmisService getCmisServiceTarget(CMISConnector connector) + { + return new AlfrescoCmisServiceImpl(connector); + } } diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java index 33c986ff74..e2d050f266 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java @@ -180,7 +180,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { // create a session -> set a cookie // if the CMIS client supports cookies that might help in clustered environments - ((HttpServletRequest) context.get(CallContext.HTTP_SERVLET_REQUEST)).getSession(); + ((HttpServletRequest) getContext().get(CallContext.HTTP_SERVLET_REQUEST)).getSession(); } // Authenticate @@ -195,8 +195,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr if (AuthenticationUtil.getFullyAuthenticatedUser() == null) { // We have to go to the repo and authenticate - String user = context.getUsername(); - String password = context.getPassword(); + String user = getContext().getUsername(); + String password = getContext().getPassword(); Authorization auth = new Authorization(user, password); if (auth.isTicket()) { @@ -213,8 +213,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // // TODO: How is the proxy user working. // // Until we know what it is meant to do, it's not available // String currentUser = connector.getAuthenticationService().getCurrentUserName(); -// String user = context.getUsername(); -// String password = context.getPassword(); +// String user = getContext().getUsername(); +// String password = getContext().getPassword(); // if (currentUser != null && currentUser.equals(connector.getProxyUser())) // { // if (user != null && user.length() > 0) @@ -236,7 +236,12 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { this.context = context; } - + + protected CallContext getContext() + { + return context; + } + @Override public void close() { @@ -580,7 +585,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData object = connector.createCMISObject(ni, child, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -707,7 +712,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr object.setObject(connector.createCMISObject( ni, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false)); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -772,7 +777,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData result = connector.createCMISObject( parentInfo, filter, false, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo( repositoryId, @@ -812,7 +817,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData object = connector.createCMISObject( parentInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, object.getId(), includeRelationships); } @@ -839,7 +844,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData object = connector.createCMISObject( parentInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, object.getId(), includeRelationships); } @@ -996,7 +1001,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ni, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -1068,7 +1073,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr throw new CmisRuntimeException("Creation failed!"); } - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { try { @@ -1496,7 +1501,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr objectId.setValue(connector.createObjectId(nodeRef)); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, objectId.getValue(), "*", IncludeRelationships.NONE); } @@ -1697,7 +1702,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr info, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } @@ -1736,7 +1741,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr info, fileInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } @@ -1758,7 +1763,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // what kind of object is it? CMISNodeInfo info = getOrCreateNodeInfo(objectId, "Object"); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, info.getObjectId(), IncludeRelationships.NONE); } @@ -1993,7 +1998,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr result.add(connector.createCMISObject(info, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, info.getObjectId(), IncludeRelationships.NONE); } @@ -2009,7 +2014,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr pwcInfo, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, pwcInfo.getObjectId(), IncludeRelationships.NONE); } @@ -2025,7 +2030,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr versionInfo, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, versionInfo.getObjectId(), IncludeRelationships.NONE); } @@ -2057,7 +2062,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr versionInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (context.isObjectInfoRequired()) + if (getContext().isObjectInfoRequired()) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } @@ -2642,7 +2647,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // -------------------------------------------------------- - private void checkRepositoryId(String repositoryId) + protected void checkRepositoryId(String repositoryId) { if (!connector.getRepositoryId().equals(repositoryId)) { diff --git a/source/java/org/alfresco/repo/action/ActionImpl.java b/source/java/org/alfresco/repo/action/ActionImpl.java index 3508b8cafc..22d0343fc1 100644 --- a/source/java/org/alfresco/repo/action/ActionImpl.java +++ b/source/java/org/alfresco/repo/action/ActionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -52,6 +52,7 @@ public class ActionImpl extends ParameterizedItemImpl implements Action private String modifier; private String actionDefinitionName; private String runAsUserName; + private String tenantId; private Set actionChain; private List actionConditions = new ArrayList(); @@ -137,6 +138,7 @@ public class ActionImpl extends ParameterizedItemImpl implements Action ActionImpl actionImpl = (ActionImpl) action; this.executionInstance = actionImpl.getExecutionInstance(); this.runAsUserName = actionImpl.getRunAsUser(); + this.tenantId = actionImpl.getTenantId(); this.actionChain = actionImpl.actionChain; } } @@ -356,11 +358,21 @@ public class ActionImpl extends ParameterizedItemImpl implements Action { return this.runAsUserName; } + + public String getTenantId() + { + return this.tenantId; + } public void setRunAsUser(String runAsUserName) { this.runAsUserName = runAsUserName; } + + public void setTenantId(String tenantId) + { + this.tenantId = tenantId; + } @Override public NodeRef getNodeRef() diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl.java b/source/java/org/alfresco/repo/action/ActionServiceImpl.java index b5dc17c2c9..0585d6a037 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -38,6 +38,7 @@ import org.alfresco.repo.copy.DefaultCopyBehaviourCallback; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; @@ -113,7 +114,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A /** * The asynchronous action execution queues map of name, queue */ - private Map asynchronousActionExecutionQueues; + private Map asynchronousActionExecutionQueues = new HashMap(); /** * Action transaction listener @@ -216,12 +217,26 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A * * @param asynchronousActionExecutionQueue the asynchronous action execution * queues + * @deprecated Rather than inject a Map, it is + * preferable to inject individual {@link AsynchronousActionExecutionQueue} instances + * during bean initialisation in a spring init-method. */ public void setAsynchronousActionExecutionQueues( Map asynchronousActionExecutionQueues) { this.asynchronousActionExecutionQueues = asynchronousActionExecutionQueues; } + + /** + * This method registers an {@link AsynchronousActionExecutionQueue} with the {@link ActionService}. + * @param key + * @param asyncExecQueue + * @since Thor Phase 2 Sprint 2 + */ + public void registerAsynchronousActionExecutionQueue(String key, AsynchronousActionExecutionQueue asyncExecQueue) + { + this.asynchronousActionExecutionQueues.put(key, asyncExecQueue); + } public void init() { @@ -777,8 +792,9 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A Action compensatingAction = action.getCompensatingAction(); if (compensatingAction != null) { - // Set the current user + // Set the current user & tenant. These should be the same for the primary action and the compensating action. ((ActionImpl) compensatingAction).setRunAsUser(currentUserName); + ((ActionImpl) compensatingAction).setTenantId(((ActionImpl)action).getTenantId()); queueAction(compensatingAction, actionedUponNodeRef); } } @@ -1612,6 +1628,13 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A } ((ActionImpl) action).setRunAsUser(this.authenticationContext.getCurrentUserName()); + // Set the tenant context to the current tenant + if (logger.isDebugEnabled() == true) + { + logger.debug("The current tenant is: " + TenantUtil.getCurrentDomain()); + } + ((ActionImpl) action).setTenantId(TenantUtil.getCurrentDomain()); + // Ensure that the transaction listener is bound to the transaction AlfrescoTransactionSupport.bindListener(this.transactionListener); diff --git a/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java b/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java index ec7f8e1d7c..6ed169e447 100644 --- a/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java +++ b/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -34,15 +34,14 @@ import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.rule.RuleServiceImpl; import org.alfresco.repo.security.authentication.AuthenticationContext; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionServiceException; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -57,11 +56,13 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE private static Log logger = LogFactory.getLog(AsynchronousActionExecutionQueueImpl.class); /** Services */ + private ActionServiceImpl actionServiceImpl; private ThreadPoolExecutor threadPoolExecutor; private TransactionService transactionService; private PolicyComponent policyComponent; private Map actionFilters = new ConcurrentHashMap(); + private String id; /** * We keep a record of ongoing asynchronous actions (this includes those being executed and @@ -85,10 +86,29 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE */ public void init() { + // Register the execution queue with the ActionService + actionServiceImpl.registerAsynchronousActionExecutionQueue(id, this); + // Register the policies onAsyncActionExecuteDelegate = policyComponent.registerClassPolicy(OnAsyncActionExecute.class); } - + + /** + * @since Thor Phase 2 Sprint 2 + */ + public void setActionServiceImpl(ActionServiceImpl serviceImpl) + { + this.actionServiceImpl = serviceImpl; + } + + /** + * @since Thor Phase 2 Sprint 2 + */ + public void setId(String id) + { + this.id = id; + } + /** * Set the thread pool, which may be shared with other components, that will be used * to run the actions. @@ -377,11 +397,13 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE final String userName = ((ActionImpl)ActionExecutionWrapper.this.action).getRunAsUser(); if (userName == null) { - throw new ActionServiceException("Cannot execute action asynchronously since run as user is 'null'"); + throw new ActionServiceException("Cannot execute action asynchronously since run as user is 'null'"); } + // Get the tenant the action was submitted from + final String tenantId = ((ActionImpl)ActionExecutionWrapper.this.action).getTenantId(); // import the content - RunAsWork actionRunAs = new RunAsWork() + TenantRunAsWork actionRunAs = new TenantRunAsWork() { public Object doWork() throws Exception { @@ -414,7 +436,7 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE return transactionService.getRetryingTransactionHelper().doInTransaction(actionCallback); } }; - AuthenticationUtil.runAs(actionRunAs, userName); + TenantUtil.runAsUserTenant(actionRunAs, userName, tenantId); } catch (Throwable e) { diff --git a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java index e078e2bb8e..7c24b1161d 100644 --- a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -155,6 +155,8 @@ public class MailActionExecuter extends ActionExecuterAbstractBase private String testMessageSubject = "Test message"; private String testMessageText = "This is a test message."; + private boolean validateAddresses = true; + /** * Test mode prevents email messages from being sent. * It is used when unit testing when we don't actually want to send out email messages. @@ -322,6 +324,22 @@ public class MailActionExecuter extends ActionExecuterAbstractBase return true; } + + /** + * This stores an email address which, if it is set, overrides ALL email recipients sent from + * this class. It is intended for dev/test usage only !! + */ + private String testModeRecipient; + + public void setTestModeRecipient(String testModeRecipient) + { + this.testModeRecipient = testModeRecipient; + } + + public void setValidateAddresses(boolean validateAddresses) + { + this.validateAddresses = validateAddresses; + } @Override @@ -600,15 +618,36 @@ public class MailActionExecuter extends ActionExecuterAbstractBase messageRef[0].setFrom(fromDefaultAddress); } - - - // set subject line messageRef[0].setSubject((String)ruleAction.getParameterValue(PARAM_SUBJECT)); + if ((testModeRecipient != null) && (testModeRecipient.length() > 0) && (! testModeRecipient.equals("${dev.email.recipient.address}"))) + { + // If we have an override for the email recipient, we'll send the email to that address instead. + // We'll prefix the subject with the original recipient, but leave the email message unchanged in every other way. + messageRef[0].setTo(testModeRecipient); + + String emailRecipient = (String)ruleAction.getParameterValue(PARAM_TO); + if (emailRecipient == null) + { + Object obj = ruleAction.getParameterValue(PARAM_TO_MANY); + if (obj != null) + { + emailRecipient = obj.toString(); + } + } + + String recipientPrefixedSubject = "(" + emailRecipient + ") " + (String)ruleAction.getParameterValue(PARAM_SUBJECT); + + messageRef[0].setSubject(recipientPrefixedSubject); + } + // See if an email template has been specified String text = null; - NodeRef templateRef = (NodeRef)ruleAction.getParameterValue(PARAM_TEMPLATE); + + // templateRef: either a nodeRef or classpath (see ClasspathRepoTemplateLoader) + Serializable ref = ruleAction.getParameterValue(PARAM_TEMPLATE); + String templateRef = (ref instanceof NodeRef ? ((NodeRef)ref).toString() : (String)ref); if (templateRef != null) { Map suppliedModel = null; @@ -630,7 +669,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase Map model = createEmailTemplateModel(actionedUponNodeRef, suppliedModel, fromPerson); // process the template against the model - text = templateService.processTemplate("freemarker", templateRef.toString(), model); + text = templateService.processTemplate("freemarker", templateRef, model); } // set the text body of the message @@ -740,7 +779,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase // Validate the email, allowing for local email addresses EmailValidator emailValidator = EmailValidator.getInstance(true); - if (emailValidator.isValid(address)) + if (!validateAddresses || emailValidator.isValid(address)) { result = true; } diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java index e12e35c862..905f705d6e 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java @@ -295,9 +295,12 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean + maxFeedItems); } + String currentTenantDomain = tenantService.getCurrentUserDomain(); + for (ActivityFeedEntity activityFeed : activityFeeds) { - if (actvityFilter != null && !actvityFilter.contains(activityFeed.getActivityType())) { + if (actvityFilter != null && !actvityFilter.contains(activityFeed.getActivityType())) + { if (logger.isTraceEnabled()) { logger.trace("Filtering " + activityFeed.toString() + " \n by the activity filter."); @@ -305,7 +308,8 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean continue; } - if (userFilter != null && !userFilter.contains(activityFeed.getPostUserId())) { + if (userFilter != null && !userFilter.contains(activityFeed.getPostUserId())) + { if (logger.isTraceEnabled()) { logger.trace("Filtering " + activityFeed.toString() + " \n by the user filter."); @@ -313,6 +317,19 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean continue; } + if (siteId == null) + { + // note: pending requirements for THOR-224, for now assume all activities are within context of site and filter by current tenant + if (! currentTenantDomain.equals(tenantService.getDomain(activityFeed.getSiteNetwork()))) + { + if (logger.isTraceEnabled()) + { + logger.trace("Filtering " + activityFeed.toString() + " \n by the site/tenant filter."); + } + continue; + } + } + // In order to prevent unnecessary 304 revalidations on user avatars in the activity stream the // activity posting user avatars will be retrieved and added to the activity feed. This will enable // avatars to be requested using the unique nodeRef which can be safely cached by the browser and diff --git a/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java b/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java index 200aa0cfc2..9797baa69e 100644 --- a/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java @@ -96,7 +96,7 @@ public abstract class AbstractUserNotifier implements UserNotifier protected abstract boolean skipUser(NodeRef personNodeRef); protected abstract Long getFeedId(NodeRef personNodeRef); - protected abstract void notifyUser(NodeRef personNodeRef, String subjectText, Map model, NodeRef templateNodeRef); + protected abstract void notifyUser(NodeRef personNodeRef, String subjectText, Map model, String templateNodeRef); private void addSiteName(String siteId, Map siteNames) { @@ -129,7 +129,7 @@ public abstract class AbstractUserNotifier implements UserNotifier } public Pair notifyUser(final NodeRef personNodeRef, String subjectText, Map siteNames, - String shareUrl, int repeatIntervalMins, NodeRef templateNodeRef) + String shareUrl, int repeatIntervalMins, String templateNodeRef) { Map personProps = nodeService.getProperties(personNodeRef); diff --git a/source/java/org/alfresco/repo/activities/feed/EmailUserNotifier.java b/source/java/org/alfresco/repo/activities/feed/EmailUserNotifier.java index a5e77715f2..a0e8110140 100644 --- a/source/java/org/alfresco/repo/activities/feed/EmailUserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/EmailUserNotifier.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2005-2012 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.activities.feed; import java.io.Serializable; @@ -142,7 +160,7 @@ public class EmailUserNotifier extends AbstractUserNotifier implements Initializ return emailFeedDBID; } - protected void notifyUser(NodeRef personNodeRef, String subjectText, Map model, NodeRef templateNodeRef) + protected void notifyUser(NodeRef personNodeRef, String subjectText, Map model, String templateNodeRef) { ParameterCheck.mandatory("personNodeRef", personNodeRef); diff --git a/source/java/org/alfresco/repo/activities/feed/ErrorProneUserNotifier.java b/source/java/org/alfresco/repo/activities/feed/ErrorProneUserNotifier.java index 7feb812d46..71c4241cf0 100644 --- a/source/java/org/alfresco/repo/activities/feed/ErrorProneUserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/ErrorProneUserNotifier.java @@ -4,7 +4,6 @@ import java.io.Serializable; import java.util.Map; import org.alfresco.model.ContentModel; -import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.repository.NodeRef; @@ -66,7 +65,7 @@ public class ErrorProneUserNotifier extends AbstractUserNotifier @Override protected void notifyUser(NodeRef personNodeRef, String subjectText, - Map model, NodeRef templateNodeRef) + Map model, String templateNodeRef) { // super.notifyUser(personNodeRef, subjectText, model, templateNodeRef); diff --git a/source/java/org/alfresco/repo/activities/feed/FeedNotifierImpl.java b/source/java/org/alfresco/repo/activities/feed/FeedNotifierImpl.java index cf71098864..57c72685ed 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedNotifierImpl.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedNotifierImpl.java @@ -36,6 +36,7 @@ import org.alfresco.repo.dictionary.RepositoryLocation; import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; import org.alfresco.repo.lock.LockAcquisitionException; +import org.alfresco.repo.search.SearcherException; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -259,30 +260,53 @@ public class FeedNotifierImpl implements FeedNotifier, ApplicationContextAware this.feedEmailTemplateLocation = feedEmailTemplateLocation; } - private NodeRef getEmailTemplateRef() + protected String getEmailTemplateRef() { - StoreRef store = feedEmailTemplateLocation.getStoreRef(); - String xpath = feedEmailTemplateLocation.getPath(); + String locationType = feedEmailTemplateLocation.getQueryLanguage(); - if (! feedEmailTemplateLocation.getQueryLanguage().equals(SearchService.LANGUAGE_XPATH)) + if (locationType.equals(SearchService.LANGUAGE_XPATH)) { - logger.warn("Cannot find the activities email template - repository location query language is not 'xpath': "+feedEmailTemplateLocation.getQueryLanguage()); + StoreRef store = feedEmailTemplateLocation.getStoreRef(); + String xpath = feedEmailTemplateLocation.getPath(); + + try + { + if (! feedEmailTemplateLocation.getQueryLanguage().equals(SearchService.LANGUAGE_XPATH)) + { + logger.error("Cannot find the activities email template - repository location query language is not 'xpath': "+feedEmailTemplateLocation.getQueryLanguage()); + return null; + } + + List nodeRefs = searchService.selectNodes(nodeService.getRootNode(store), xpath, null, namespaceService, false); + if (nodeRefs.size() != 1) + { + logger.error("Cannot find the activities email template: "+xpath); + return null; + } + + return fileFolderService.getLocalizedSibling(nodeRefs.get(0)).toString(); + } + catch (SearcherException e) + { + logger.error("Cannot find the email template!", e); + } + return null; } - - List nodeRefs = searchService.selectNodes(nodeService.getRootNode(store), xpath, null, namespaceService, false); - if (nodeRefs.size() != 1) + else if (locationType.equals(RepositoryLocation.LANGUAGE_CLASSPATH)) { - logger.warn("Cannot find the activities email template: "+xpath); + return feedEmailTemplateLocation.getPath(); + } + else + { + logger.error("Unsupported location type: "+locationType); return null; } - - return fileFolderService.getLocalizedSibling(nodeRefs.get(0)); } private void executeInternal(final int repeatIntervalMins) { - final NodeRef emailTemplateRef = getEmailTemplateRef(); + final String emailTemplateRef = getEmailTemplateRef(); if (emailTemplateRef == null) { diff --git a/source/java/org/alfresco/repo/activities/feed/MockUserNotifier.java b/source/java/org/alfresco/repo/activities/feed/MockUserNotifier.java index 71d473eb9a..0d9437fa22 100644 --- a/source/java/org/alfresco/repo/activities/feed/MockUserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/MockUserNotifier.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2005-2012 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.activities.feed; import java.io.Serializable; @@ -49,7 +67,7 @@ public class MockUserNotifier extends AbstractUserNotifier } @Override - protected void notifyUser(NodeRef personNodeRef, String subjectText, Map model, NodeRef templateNodeRef) + protected void notifyUser(NodeRef personNodeRef, String subjectText, Map model, String templateNodeRef) { String username = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); if(username.startsWith("user")) diff --git a/source/java/org/alfresco/repo/activities/feed/UserNotifier.java b/source/java/org/alfresco/repo/activities/feed/UserNotifier.java index 423d639c5f..c754b49bc2 100644 --- a/source/java/org/alfresco/repo/activities/feed/UserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/UserNotifier.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2005-2012 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.activities.feed; import java.util.Map; @@ -13,5 +31,5 @@ import org.alfresco.util.Pair; public interface UserNotifier { public Pair notifyUser(final NodeRef personNodeRef, String subjectText, Map siteNames, - String shareUrl, int repeatIntervalMins, NodeRef templateNodeRef); + String shareUrl, int repeatIntervalMins, String templateNodeRef); } diff --git a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java b/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java index 58ea68216b..77db299e16 100644 --- a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java +++ b/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java @@ -19,9 +19,11 @@ package org.alfresco.repo.activities.feed.cleanup; import java.sql.SQLException; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.alfresco.model.ContentModel; @@ -35,6 +37,7 @@ import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.TransactionListenerAdapter; @@ -70,6 +73,7 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy private JobLockService jobLockService; private NodeService nodeService; + private TenantService tenantService; private PolicyComponent policyComponent; private TransactionService transactionService; @@ -92,6 +96,11 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy this.nodeService = nodeService; } + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -424,7 +433,13 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy { String userId = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); - Set deletedUserIds = TransactionalResourceHelper.getSet(KEY_DELETED_USER_IDS); + Set deletedUserIds = (Set)AlfrescoTransactionSupport.getResource(KEY_DELETED_USER_IDS); + if (deletedUserIds == null) + { + deletedUserIds = Collections.newSetFromMap(new ConcurrentHashMap()); // Java 6 + AlfrescoTransactionSupport.bindResource(KEY_DELETED_USER_IDS, deletedUserIds); + } + deletedUserIds.add(userId); AlfrescoTransactionSupport.bindListener(deletePersonTransactionListener); @@ -434,7 +449,13 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy { String siteId = (String)nodeService.getProperty(siteNodeRef, ContentModel.PROP_NAME); - Set deletedSiteIds = TransactionalResourceHelper.getSet(KEY_DELETED_SITE_IDS); + Set deletedSiteIds = (Set)AlfrescoTransactionSupport.getResource(KEY_DELETED_SITE_IDS); + if (deletedSiteIds == null) + { + deletedSiteIds = Collections.newSetFromMap(new ConcurrentHashMap()); // Java 6 + AlfrescoTransactionSupport.bindResource(KEY_DELETED_SITE_IDS, deletedSiteIds); + } + deletedSiteIds.add(siteId); AlfrescoTransactionSupport.bindListener(deleteSiteTransactionListener); diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java index 5b5520098b..e1721e4d36 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -40,6 +40,7 @@ import org.alfresco.repo.domain.activities.FeedControlEntity; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.template.ClassPathRepoTemplateLoader; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -277,7 +278,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica final String siteId = tenantService.getBaseName(siteIdIn, true); // optimise for non-remote implementation - override remote repo callback (to "List Site Memberships" web script) with embedded call - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + return TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork>() { public Set doWork() throws Exception { @@ -301,7 +302,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica return members; } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } } @@ -372,13 +373,13 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica String tenantDomain = (String)model.get(PostLookup.JSON_TENANT_DOMAIN); if (tenantDomain == null) { tenantDomain = TenantService.DEFAULT_DOMAIN; } - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + return TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork() { public Boolean doWork() throws Exception { return canReadImpl(connectedUser, nodeRef); } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } else { @@ -585,7 +586,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica if (subscriptionService.isActive()) { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + TenantUtil.runAsSystemTenant(new TenantUtil.TenantRunAsWork() { public Void doWork() throws Exception { @@ -598,7 +599,7 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica return null; } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } return result; diff --git a/source/java/org/alfresco/repo/admin/patch/impl/UpdateWorkflowNotificationTemplatesPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/UpdateWorkflowNotificationTemplatesPatch.java index afef4fe681..c200419ddc 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/UpdateWorkflowNotificationTemplatesPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/UpdateWorkflowNotificationTemplatesPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -54,7 +54,7 @@ public class UpdateWorkflowNotificationTemplatesPatch extends GenericEMailTempla @Override protected NodeRef getBaseTemplate() { - return WorkflowNotificationUtils.WF_ASSIGNED_TEMPLATE; + return new NodeRef(WorkflowNotificationUtils.WF_ASSIGNED_TEMPLATE); } /** diff --git a/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java b/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java index 59605089f8..c35df9c4c3 100644 --- a/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java +++ b/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -173,9 +173,9 @@ public class AttributeServiceImpl implements AttributeService } else { - Long id = pair.getFirst(); - propertyValueDAO.updatePropertyUniqueContext(id, value); + propertyValueDAO.updatePropertyUniqueContext(keys[0], keys[1], keys[2], value); } + // Done if (logger.isDebugEnabled()) { @@ -231,7 +231,7 @@ public class AttributeServiceImpl implements AttributeService else { Long id = pair.getFirst(); - propertyValueDAO.updatePropertyUniqueContext(id, keyAfter1, keyAfter2, keyAfter3); + propertyValueDAO.updatePropertyUniqueContextKeys(id, keyAfter1, keyAfter2, keyAfter3); } } catch (PropertyUniqueConstraintViolation e) diff --git a/source/java/org/alfresco/repo/audit/AuditComponentTest.java b/source/java/org/alfresco/repo/audit/AuditComponentTest.java index 483d44dbf0..1182b0c58e 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentTest.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentTest.java @@ -46,7 +46,6 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.MutableAuthenticationService; -import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.EqualsHelper; @@ -54,7 +53,6 @@ import org.apache.commons.lang.mutable.MutableInt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; -import org.springframework.extensions.webscripts.GUID; import org.springframework.util.ResourceUtils; /** diff --git a/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java index 12c538811f..880c6f1a98 100644 --- a/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java +++ b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -24,8 +24,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import javax.transaction.UserTransaction; - import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.security.authentication.AuthenticationContext; @@ -33,6 +31,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantDeployer; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; @@ -430,7 +429,7 @@ public class RepoXMLConfigService extends XMLConfigService implements TenantDepl // local helper - returns tenant domain (or empty string if default non-tenant) private String getTenantDomain() { - return tenantAdminService.getCurrentUserDomain(); + return TenantUtil.getCurrentDomain(); } private static class ConfigData diff --git a/source/java/org/alfresco/repo/dictionary/RepositoryLocation.java b/source/java/org/alfresco/repo/dictionary/RepositoryLocation.java index 69479b1a87..4af4621b15 100644 --- a/source/java/org/alfresco/repo/dictionary/RepositoryLocation.java +++ b/source/java/org/alfresco/repo/dictionary/RepositoryLocation.java @@ -24,9 +24,7 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; /** - * Repository location object, defines a location in the repository from which dictionary models/resources should be loaded - * for inclusion in the data dictionary. - * + * Repository location object - defines a location in the repository (can also be used for classpath location) */ public class RepositoryLocation { @@ -43,6 +41,7 @@ public class RepositoryLocation private String queryLanguage = "xpath"; // default public static final String LANGUAGE_PATH = "path"; // lookup directly using node (prefix qname) path + public static final String LANGUAGE_CLASSPATH = "classpath"; /** @@ -106,7 +105,10 @@ public class RepositoryLocation */ public void setQueryLanguage(String queryLanguage) { - if (queryLanguage.equals(SearchService.LANGUAGE_LUCENE) || queryLanguage.equals(SearchService.LANGUAGE_XPATH) || queryLanguage.equals(LANGUAGE_PATH)) + if (queryLanguage.equals(SearchService.LANGUAGE_LUCENE) || + queryLanguage.equals(SearchService.LANGUAGE_XPATH) || + queryLanguage.equals(LANGUAGE_PATH) || + queryLanguage.equals(LANGUAGE_CLASSPATH)) { this.queryLanguage = queryLanguage; } @@ -117,7 +119,7 @@ public class RepositoryLocation } /** - * Get the store reference + * Get the store reference (note: should be ignored for classpath location) * * @return the store reference */ diff --git a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMLockDAOImpl.java b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMLockDAOImpl.java index b22044bc60..c03ff31be7 100644 --- a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMLockDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMLockDAOImpl.java @@ -21,7 +21,10 @@ package org.alfresco.repo.domain.avm.ibatis; import java.util.HashMap; import java.util.Map; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.avm.AbstractAVMLockDAOImpl; +import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl.CachePucKey; +import org.alfresco.repo.domain.propval.PropertyUniqueContextEntity; import org.mybatis.spring.SqlSessionTemplate; /** @@ -43,6 +46,18 @@ public class AVMLockDAOImpl extends AbstractAVMLockDAOImpl this.template = sqlSessionTemplate; } + private SimpleCache propertyUniqueContextCache; // cluster-aware + + /** + * Set the cache to use for avm_version_roots lookups (optional). + * + * @param vrEntityCache + */ + public void setPropertyUniqueContextCache(SimpleCache propertyUniqueContextCache) + { + this.propertyUniqueContextCache = propertyUniqueContextCache; + } + @Override protected int deletePropertyUniqueContexts(Long avmLocksValueId, Long avmStoreNameId, String dirPathToMatch, String lockDataStoreKey, String lockDataStoreValue) { @@ -55,25 +70,34 @@ public class AVMLockDAOImpl extends AbstractAVMLockDAOImpl dirPathToMatch = dirPathToMatch + "%"; } - if (lockDataStoreKey != null) + try { - Map params = new HashMap(5); - params.put("value1PropId", avmLocksValueId); - params.put("value2PropId", avmStoreNameId); - params.put("value3LikeStr", dirPathToMatch); - params.put("lockDataStoreKey", lockDataStoreKey); - params.put("lockDataStoreValue", lockDataStoreValue); - - return template.delete(DELETE_MATCHING_AVM_LOCKS_1_KV, params); + if (lockDataStoreKey != null) + { + Map params = new HashMap(5); + params.put("value1PropId", avmLocksValueId); + params.put("value2PropId", avmStoreNameId); + params.put("value3LikeStr", dirPathToMatch); + params.put("lockDataStoreKey", lockDataStoreKey); + params.put("lockDataStoreValue", lockDataStoreValue); + + return template.delete(DELETE_MATCHING_AVM_LOCKS_1_KV, params); + } + else + { + Map params = new HashMap(3); + params.put("value1PropId", avmLocksValueId); + params.put("value2PropId", avmStoreNameId); + params.put("value3LikeStr", dirPathToMatch); + + return template.delete(DELETE_MATCHING_AVM_LOCKS_0_KV, params); + } } - else + finally { - Map params = new HashMap(3); - params.put("value1PropId", avmLocksValueId); - params.put("value2PropId", avmStoreNameId); - params.put("value3LikeStr", dirPathToMatch); - - return template.delete(DELETE_MATCHING_AVM_LOCKS_0_KV, params); + // reasonable to clear for now (eg. only used by AVMLockingService.removeLocks*) + // note: in future, if we need to support mass removal based on specific key grouping then we need to use more intelligent cache (removal) + propertyUniqueContextCache.clear(); } } } diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 4fbaeda1f1..551554e688 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -690,21 +690,21 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO /* * Stores */ - + @Override public Pair getStore(StoreRef storeRef) { - StoreEntity store = selectStore(storeRef); - if (store == null) + Pair rootNodePair = rootNodesCache.getByKey(storeRef); + if (rootNodePair == null) { - return null; + throw new InvalidStoreRefException(storeRef); } else { - return new Pair(store.getId(), store.getStoreRef()); + return new Pair(rootNodePair.getSecond().getStore().getId(), rootNodePair.getFirst()); } } - + @Override public List> getStores() { diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java index f8cfb45b5e..4dfe46dc3f 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * diff --git a/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java index 42156e1bc9..0248fe97e5 100644 --- a/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/propval/AbstractPropertyValueDAOImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -31,6 +31,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import org.alfresco.repo.cache.NullCache; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.lookup.EntityLookupCache; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor; @@ -38,9 +39,11 @@ import org.alfresco.repo.domain.CrcHelper; import org.alfresco.repo.domain.control.ControlDAO; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; import org.alfresco.repo.domain.schema.SchemaBootstrap; +import org.alfresco.util.EqualsHelper; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.DataIntegrityViolationException; /** @@ -124,12 +127,26 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO */ private EntityLookupCache propertyCache; + private SimpleCache propertyUniqueContextCache; // cluster-aware + + /** + * Set the cache to use for avm_version_roots lookups (optional). + * + * @param vrEntityCache + */ + public void setPropertyUniqueContextCache(SimpleCache propertyUniqueContextCache) + { + this.propertyUniqueContextCache = propertyUniqueContextCache; + } + + /** * Default constructor. *

* This sets up the DAO accessors to bypass any caching to handle the case where the caches are not * supplied in the setters. */ + @SuppressWarnings({ "unchecked", "rawtypes" }) public AbstractPropertyValueDAOImpl() { this.propertyClassDaoCallback = new PropertyClassCallbackDAO(); @@ -147,6 +164,8 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO this.propertySerializableValueCache = new EntityLookupCache(propertySerializableValueCallback); this.propertyValueCache = new EntityLookupCache(propertyValueCallback); this.propertyCache = new EntityLookupCache(propertyCallback); + + this.propertyUniqueContextCache = (SimpleCache)new NullCache(); } /** @@ -805,6 +824,9 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO Pair entityPair = propertyCache.getByKey(id); if (entityPair == null) { + // Remove from cache + propertyCache.removeByKey(id); + throw new DataIntegrityViolationException("No property value exists for ID " + id); } return entityPair.getSecond(); @@ -1085,7 +1107,63 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO //================================ // 'alf_prop_unique_ctx' accessors //================================ - + + private CachePucKey getPucKey(Long id1, Long id2, Long id3) + { + return new CachePucKey(id1, id2, id3); + } + + /** + * Key for PropertyUniqueContext cache + */ + public static class CachePucKey implements Serializable + { + private static final long serialVersionUID = -4294324585692613101L; + + private final Long key1; + private final Long key2; + private final Long key3; + + private final int hashCode; + + private CachePucKey(Long key1, Long key2, Long key3) + { + this.key1 = key1; + this.key2 = key2; + this.key3 = key3; + this.hashCode = (key1 == null ? 0 : key1.hashCode()) + (key2 == null ? 0 : key2.hashCode()) + (key3 == null ? 0 : key3.hashCode()); + } + + @Override + public String toString() + { + return key1 + "." + key2 + "." + key3; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (!(obj instanceof CachePucKey)) + { + return false; + } + CachePucKey that = (CachePucKey) obj; + return EqualsHelper.nullSafeEquals(this.key1, that.key1) && + EqualsHelper.nullSafeEquals(this.key2, that.key2) && + EqualsHelper.nullSafeEquals(this.key3, that.key3); + } + + @Override + public int hashCode() + { + return hashCode; + } + } + public Pair createPropertyUniqueContext( Serializable value1, Serializable value2, Serializable value3, Serializable propertyValue1) @@ -1104,11 +1182,17 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO property1Id = createProperty(propertyValue1); } + CachePucKey pucKey = getPucKey(id1, id2, id3); + Savepoint savepoint = controlDAO.createSavepoint("createPropertyUniqueContext"); try { PropertyUniqueContextEntity entity = createPropertyUniqueContext(id1, id2, id3, property1Id); controlDAO.releaseSavepoint(savepoint); + + // cache + propertyUniqueContextCache.put(pucKey, entity); + if (logger.isDebugEnabled()) { logger.debug( @@ -1116,10 +1200,14 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO " Values: " + value1 + "-" + value2 + "-" + value3 + "\n" + " Result: " + entity); } + return new Pair(entity.getId(), property1Id); } catch (Throwable e) { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + controlDAO.rollbackToSavepoint(savepoint); throw new PropertyUniqueConstraintViolation(value1, value2, value3, e); } @@ -1132,14 +1220,48 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO Pair pair2 = getPropertyValue(value2); Pair pair3 = getPropertyValue(value3); if (pair1 == null || pair2 == null || pair3 == null) - { + { // None of the values exist so no unique context values can exist - return null; - } + return null; + } Long id1 = pair1.getFirst(); Long id2 = pair2.getFirst(); Long id3 = pair3.getFirst(); - PropertyUniqueContextEntity entity = getPropertyUniqueContextByValues(id1, id2, id3); + + CachePucKey pucKey = getPucKey(id1, id2, id3); + + // check cache + PropertyUniqueContextEntity entity = propertyUniqueContextCache.get(pucKey); + if (entity == null) + { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + + // query DB + entity = getPropertyUniqueContextByValues(id1, id2, id3); + + if (entity != null) + { + // cache + propertyUniqueContextCache.put(pucKey, entity); + } + } + + if ((entity != null) && (entity.getPropertyId() != null)) + { + try + { + // eager fetch - ignore return for now (could change API) + getPropertyById(entity.getPropertyId()); + } + catch (DataIntegrityViolationException dive) + { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + throw dive; + } + } + // Done if (logger.isDebugEnabled()) { @@ -1168,7 +1290,10 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO } valueIds[i] = valuePair.getFirst(); } + + // not cached getPropertyUniqueContextByValues(callback, valueIds); + // Done if (logger.isDebugEnabled()) { @@ -1177,8 +1302,12 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO " Values: " + Arrays.toString(values)); } } - - public void updatePropertyUniqueContext(Long id, Serializable value1, Serializable value2, Serializable value3) + + /* + * Update PUC keys - retain current property value + * + */ + public void updatePropertyUniqueContextKeys(Long id, Serializable value1, Serializable value2, Serializable value3) { /* * Use savepoints so that the PropertyUniqueConstraintViolation can be caught and handled in-transactioin @@ -1189,19 +1318,30 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO Long id2 = getOrCreatePropertyValue(value2).getFirst(); Long id3 = getOrCreatePropertyValue(value3).getFirst(); + CachePucKey pucKey = getPucKey(id1, id2, id3); + Savepoint savepoint = controlDAO.createSavepoint("updatePropertyUniqueContext"); try { PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id); if (entity == null) { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + throw new DataIntegrityViolationException("No unique property context exists for id: " + id); } entity.setValue1PropId(id1); entity.setValue2PropId(id2); entity.setValue3PropId(id3); - updatePropertyUniqueContext(entity); + + entity = updatePropertyUniqueContext(entity); + controlDAO.releaseSavepoint(savepoint); + + // cache + propertyUniqueContextCache.put(pucKey, entity); + // Done if (logger.isDebugEnabled()) { @@ -1214,43 +1354,83 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO } catch (Throwable e) { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + controlDAO.rollbackToSavepoint(savepoint); throw new PropertyUniqueConstraintViolation(value1, value2, value3, e); } } - - public void updatePropertyUniqueContext(Long id, Serializable propertyValue) + + /* + * Update property value by keys + */ + public void updatePropertyUniqueContext(Serializable value1, Serializable value2, Serializable value3, Serializable propertyValue) { - PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id); - if (entity == null) - { - throw new DataIntegrityViolationException("No unique property context exists for id: " + id); - } - Long propertyIdToDelete = entity.getPropertyId(); - - Long propertyId = null; - if (propertyValue != null) - { - propertyId = createProperty(propertyValue); - } + // Translate the properties. Null values are acceptable + Long id1 = getOrCreatePropertyValue(value1).getFirst(); + Long id2 = getOrCreatePropertyValue(value2).getFirst(); + Long id3 = getOrCreatePropertyValue(value3).getFirst(); - // Create a new property - entity.setPropertyId(propertyId); - updatePropertyUniqueContext(entity); + CachePucKey pucKey = getPucKey(id1, id2, id3); - // Clean up the previous property, if present - if (propertyIdToDelete != null) + try { - deleteProperty(propertyIdToDelete); + Pair entityPair = getPropertyUniqueContext(value1, value2, value3); + if (entityPair == null) + { + throw new DataIntegrityViolationException("No unique property context exists for values: " + value1 + "-" + value2 + "-" + value3); + } + + long id = entityPair.getFirst(); + PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id); + if (entity == null) + { + throw new DataIntegrityViolationException("No unique property context exists for id: " + id); + } + + Long propertyIdToDelete = entity.getPropertyId(); + + Long propertyId = null; + if (propertyValue != null) + { + propertyId = createProperty(propertyValue); + } + + // Create a new property + entity.setPropertyId(propertyId); + + entity = updatePropertyUniqueContext(entity); + + // cache + propertyUniqueContextCache.put(pucKey, entity); + + // Clean up the previous property, if present + if (propertyIdToDelete != null) + { + deleteProperty(propertyIdToDelete); + } + + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Updated unique property context: \n" + + " ID: " + id + "\n" + + " Property: " + propertyId); + } } - - // Done - if (logger.isDebugEnabled()) + catch (DataIntegrityViolationException e) { - logger.debug( - "Updated unique property context: \n" + - " ID: " + id + "\n" + - " Property: " + propertyId); + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + throw e; + } + catch (ConcurrencyFailureException e) + { + // Remove from cache + propertyUniqueContextCache.remove(pucKey); + throw e; } } @@ -1272,6 +1452,20 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO valueIds[i] = valuePair.getFirst(); } int deleted = deletePropertyUniqueContexts(valueIds); + + CachePucKey pucKey = getPucKey(valueIds[0], (values.length > 1 ? valueIds[1] : null), (values.length > 2 ? valueIds[2] : null)); + + if (values.length == 3) + { + propertyUniqueContextCache.remove(pucKey); + } + else + { + // reasonable to clear for now (eg. only used by AVMLockingService.removeLocks*) + // note: in future, if we need to support mass removal based on specific key grouping then we need to use more intelligent cache (removal) + propertyUniqueContextCache.clear(); + } + // Done if (logger.isDebugEnabled()) { diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java index 3920a78471..be3b223aad 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -325,15 +325,15 @@ public interface PropertyValueDAO * * @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable) */ - void updatePropertyUniqueContext( + void updatePropertyUniqueContextKeys( Long id, Serializable value1, Serializable value2, Serializable value3); /** - * Update the property associated with a unique context. + * Update the property associated with a unique context (based on one, two or three context values). * * @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable) */ - void updatePropertyUniqueContext(Long id, Serializable propertyValue1); + void updatePropertyUniqueContext(Serializable value1, Serializable value2, Serializable value3, Serializable propertyValue); /** * @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable) */ diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java index 7d999acde9..33ea3473bb 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -777,7 +777,7 @@ public class PropertyValueDAOTest extends TestCase public Void execute() throws Throwable { // Now update it - propertyValueDAO.updatePropertyUniqueContext(id, "A", "AA", bbb); + propertyValueDAO.updatePropertyUniqueContextKeys(id, "A", "AA", bbb); // Should be able to create the previous one ... propertyValueDAO.createPropertyUniqueContext("A", "AA", aaa, null); // ... and fail to create the second one @@ -851,7 +851,7 @@ public class PropertyValueDAOTest extends TestCase { public Pair execute() throws Throwable { - propertyValueDAO.updatePropertyUniqueContext(id, v1); + propertyValueDAO.updatePropertyUniqueContext(aaa, bbb, ccc, v1); Pair pair = propertyValueDAO.getPropertyUniqueContext(aaa, bbb, ccc); Serializable value = propertyValueDAO.getPropertyById(pair.getSecond()); return new Pair(pair.getFirst(), value); @@ -863,7 +863,7 @@ public class PropertyValueDAOTest extends TestCase { public Pair execute() throws Throwable { - propertyValueDAO.updatePropertyUniqueContext(id, v2); + propertyValueDAO.updatePropertyUniqueContext(aaa, bbb, ccc, v2); Pair pair = propertyValueDAO.getPropertyUniqueContext(aaa, bbb, ccc); Serializable value = propertyValueDAO.getPropertyById(pair.getSecond()); return new Pair(pair.getFirst(), value); diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java index 36978aa45b..b66f4ac426 100644 --- a/source/java/org/alfresco/repo/jscript/People.java +++ b/source/java/org/alfresco/repo/jscript/People.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.StringTokenizer; import org.alfresco.model.ContentModel; -import org.alfresco.query.PagingRequest; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.UserNameGenerator; @@ -56,6 +55,7 @@ import org.alfresco.service.cmr.usage.ContentUsageService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; +import org.alfresco.util.ScriptPagingDetails; import org.alfresco.util.ValueDerivingMapFactory; import org.alfresco.util.ValueDerivingMapFactory.ValueDeriver; import org.apache.commons.logging.Log; @@ -72,7 +72,7 @@ import org.springframework.extensions.surf.util.ParameterCheck; * @author davidc * @author kevinr */ -public final class People extends BaseScopableProcessorExtension implements InitializingBean +public class People extends BaseScopableProcessorExtension implements InitializingBean { private static Log logger = LogFactory.getLog(People.class); @@ -83,9 +83,10 @@ public final class People extends BaseScopableProcessorExtension implements Init private PersonService personService; private MutableAuthenticationService authenticationService; private ContentUsageService contentUsageService; - private TenantService tenantService; private UserNameGenerator usernameGenerator; private UserRegistrySynchronizer userRegistrySynchronizer; + protected TenantService tenantService; + private StoreRef storeRef; private ValueDerivingMapFactory valueDerivingMapFactory; private int numRetries = 10; @@ -535,6 +536,26 @@ public final class People extends BaseScopableProcessorExtension implements Init */ public Scriptable getPeople(String filter, int maxResults, String sortBy, boolean sortAsc) { + return getPeople(filter, new ScriptPagingDetails(maxResults, 0), null, null); + } + + public Scriptable getPeople(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc) + { + List persons = getPeopleImpl(filter, pagingRequest, sortBy, sortAsc); + + Object[] peopleRefs = new Object[persons.size()]; + for (int i = 0; i < peopleRefs.length; i++) + { + peopleRefs[i] = persons.get(i).getNodeRef(); + } + + return Context.getCurrentContext().newArray(getScope(), peopleRefs); + } + + protected List getPeopleImpl(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc) + { + ParameterCheck.mandatory("pagingRequest", pagingRequest); + boolean useCQ = false; if (filter != null) { @@ -544,96 +565,124 @@ public final class People extends BaseScopableProcessorExtension implements Init filter = filter.substring(0, filter.length()-HINT_CQ_SUFFIX.length()); } } + else + { + filter = "*"; + } - Object[] people = null; + List persons = null; + int maxResults = pagingRequest.getMaxItems(); if ((maxResults <= 0) || (maxResults > defaultListMaxResults)) { // remove open-ended query (eg cutoff at default/configurable max, eg. 5000 people) maxResults = defaultListMaxResults; + pagingRequest.setMaxItems(maxResults); } - if ((filter == null || filter.length() == 0) || useCQ) + if (useCQ) { - people = getPeopleImplDB(filter, maxResults, sortBy, sortAsc); + persons = getPeopleImplDB(filter, pagingRequest, sortBy, sortAsc); } else { filter = filter.trim(); - if (filter.length() != 0) + + String term = filter.replace("\\", "").replace("\"", ""); + StringTokenizer t = new StringTokenizer(term, " "); + int propIndex = term.lastIndexOf(':'); + int wildPosition = term.indexOf('*'); + + // simple filter - can use CQ if search fails + useCQ = ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1)))); + + try { - String term = filter.replace("\\", "").replace("\"", ""); - StringTokenizer t = new StringTokenizer(term, " "); - int propIndex = term.lastIndexOf(':'); - int wildPosition = term.indexOf('*'); + // FTS + List personRefs = getPeopleImplSearch(filter, pagingRequest, sortBy, sortAsc); - // simple filter - can use CQ if search fails - useCQ = ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1)))); - - try + if (personRefs != null) { - people = getPeopleImplSearch(term, t, propIndex, maxResults, sortBy, sortAsc); - } - catch (Throwable err) - { - if (useCQ) + persons = new ArrayList(personRefs.size()); + for (NodeRef personRef : personRefs) { - // search unavailable and/or parser exception - try CQ instead - people = getPeopleImplDB(term, maxResults, sortBy, sortAsc); + persons.add(personService.getPerson(personRef)); } } } + catch (Throwable err) + { + if (useCQ) + { + // search unavailable and/or parser exception - try CQ instead + // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case) + persons = getPeopleImplDB(filter, pagingRequest, sortBy, sortAsc); + } + } } - if (people == null) - { - people = new Object[0]; - } - - return Context.getCurrentContext().newArray(getScope(), people); + return (persons != null ? persons : new ArrayList(0)); } // canned query - private Object[] getPeopleImplDB(String term, int maxResults, final String sortBy, boolean sortAsc) + protected List getPeopleImplDB(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc) { - Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + List filterProps = null; - List peopleRefs = new ArrayList(); - - // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case) - - List filterProps = new ArrayList(3); - filterProps.add(ContentModel.PROP_FIRSTNAME); - filterProps.add(ContentModel.PROP_LASTNAME); - filterProps.add(ContentModel.PROP_USERNAME); - - List> sortProps = new ArrayList>(1); - sortProps.add(new Pair(ContentModel.PROP_USERNAME, true)); - - PagingRequest pagingRequest = new PagingRequest(maxResults, null); - List persons = personService.getPeople(term, filterProps, sortProps, pagingRequest).getPage(); - for (int i=0; i 0)) { - peopleRefs.add(persons.get(i).getNodeRef()); + filter = filter.trim(); + if (! filter.equals("*")) + { + filter = filter.replace("\\", "").replace("\"", ""); + + // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case) + + filterProps = new ArrayList(3); + filterProps.add(ContentModel.PROP_FIRSTNAME); + filterProps.add(ContentModel.PROP_LASTNAME); + filterProps.add(ContentModel.PROP_USERNAME); + } } - Object[] people = getSortedPeopleObjects(peopleRefs, sortBy, sortAsc); - - if (start != null) + // Build the sorting. The user controls the primary sort, we supply + // additional ones automatically + List> sort = new ArrayList>(); + if ("lastName".equals(sortBy)) { - logger.debug("getPeople: cq - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)"); + sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc)); } - - return people; + else if ("firstName".equals(sortBy)) + { + sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc)); + } + else + { + sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc)); + sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc)); + } + + return personService.getPeople(filter, filterProps, sort, pagingRequest).getPage(); } // search query - private Object[] getPeopleImplSearch(String term, StringTokenizer t, int propIndex, int maxResults, final String sortBy, boolean sortAsc) throws Throwable + protected List getPeopleImplSearch(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc) throws Throwable { + List personRefs = null; + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); - List peopleRefs = new ArrayList(); - Object[] people = null; + String term = filter.replace("\\", "").replace("\"", ""); + StringTokenizer t = new StringTokenizer(term, " "); + int propIndex = term.indexOf(':'); + + int maxResults = pagingRequest.getMaxItems(); + int skipCount = pagingRequest.getSkipCount(); SearchParameters params = new SearchParameters(); params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName"); @@ -732,21 +781,57 @@ public final class People extends BaseScopableProcessorExtension implements Init params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); params.addStore(this.storeRef); params.setQuery(query.toString()); + + if (logger.isDebugEnabled()) + { + if ((sortBy != null) && (! sortBy.isEmpty())) + { + logger.debug("getPeopleImplSearch: ignoring sortBy ("+sortBy+")- not yet supported by model for search"); + } + } + + /* not yet supported (default property index tokenisation mode = true) + if ("lastName".equals(sortBy)) + { + params.addSort("@{http://www.alfresco.org/model/content/1.0}lastName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc); + } + else if ("firstName".equals(sortBy)) + { + params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}lastName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc); + } + else + { + params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc); + params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc); + } + */ + if (maxResults > 0) { params.setLimitBy(LimitBy.FINAL_SIZE); params.setLimit(maxResults); } + if (skipCount > 0) + { + params.setSkipCount(skipCount); + } + ResultSet results = null; try { results = services.getSearchService().query(params); - people = getSortedPeopleObjects(results.getNodeRefs(), sortBy, sortAsc); + + personRefs = getSortedPeopleObjects(results.getNodeRefs(), sortBy, sortAsc); if (start != null) { - logger.debug("getPeople: search - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)"); + logger.debug("getPeople: search - "+personRefs.size()+" items (in "+(System.currentTimeMillis()-start)+" msecs)"); } } catch (Throwable err) @@ -766,10 +851,10 @@ public final class People extends BaseScopableProcessorExtension implements Init } } - return people; + return personRefs; } - private Object[] getSortedPeopleObjects(List peopleRefs, final String sortBy, boolean sortAsc) + private List getSortedPeopleObjects(List peopleRefs, final String sortBy, boolean sortAsc) { final Collator col = Collator.getInstance(I18NUtil.getLocale()); final NodeService nodeService = services.getNodeService(); @@ -831,12 +916,9 @@ public final class People extends BaseScopableProcessorExtension implements Init return result.toString(); } - }); + }); - Object[] people = new Object[peopleRefs.size()]; - peopleRefs.toArray(people); - - return people; + return peopleRefs; } /** diff --git a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java index 1ec22b0f0d..88e2680cf8 100644 --- a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java +++ b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2005-2012 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.model.filefolder; import java.io.Serializable; @@ -15,6 +33,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -599,42 +618,44 @@ public class HiddenAspect { Visibility ret = Visibility.Visible; - if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN)) + if (! AuthenticationUtil.isRunAsUserTheSystemUser()) { - Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK); - if(visibilityMask != null) + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN)) { - if(visibilityMask.intValue() == 0) + Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK); + if (visibilityMask != null) { - ret = Visibility.NotVisible; - } - else if(client == null) - { - ret = Visibility.NotVisible; + if(visibilityMask.intValue() == 0) + { + ret = Visibility.NotVisible; + } + else if(client == null) + { + ret = Visibility.NotVisible; + } + else + { + ret = getVisibility(visibilityMask.intValue(), client); + } } else { - ret = getVisibility(visibilityMask.intValue(), client); - } - } - else - { - // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour - if(client == Client.cifs) - { - ret = Visibility.HiddenAttribute; - } - else if(client == Client.webdav || client == Client.nfs || client == Client.imap) - { - ret = Visibility.Visible; - } - else - { - ret = Visibility.NotVisible; + // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour + if(client == Client.cifs) + { + ret = Visibility.HiddenAttribute; + } + else if(client == Client.webdav || client == Client.nfs || client == Client.imap) + { + ret = Visibility.Visible; + } + else + { + ret = Visibility.NotVisible; + } } } } - return ret; } diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java index df19604f41..fbe647cb8b 100644 --- a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java +++ b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java @@ -87,6 +87,7 @@ public class ModuleComponentHelper private RegistryService registryService; private ModuleService moduleService; private TenantAdminService tenantAdminService; + private boolean applyToTenants; private Map> componentsByNameByModule; /** Default constructor */ @@ -131,7 +132,12 @@ public class ModuleComponentHelper { this.tenantAdminService = tenantAdminService; } - + + public void setApplyToTenants(boolean applyToTenants) + { + this.applyToTenants = applyToTenants; + } + /** * Add a managed module component to the registry of components. These will be controlled * by the {@link #startModules()} method. @@ -197,40 +203,47 @@ public class ModuleComponentHelper */ AuthenticationUtil.runAs(new RunAsWork() { - public Object doWork() throws Exception + public Object doWork() throws Exception { - try - { - TransactionService transactionService = serviceRegistry.getTransactionService(); - - // Get all the modules - List modules = moduleService.getAllModules(); - loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); + try + { + TransactionService transactionService = serviceRegistry.getTransactionService(); + + // Note: for system bootstrap this will be the default domain, else tenant domain for tenant create/import + final String tenantDomainCtx = tenantAdminService.getCurrentUserDomain(); + + if (tenantAdminService.isEnabled() && (! tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN)) && (! applyToTenants)) + { + // nothing to start (eg. when creating/importing tenant and applyToTenants = false) + return null; + } + + // Get all the modules + List modules = moduleService.getAllModules(); + loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); + + // Process each module in turn. Ordering is not important. + final Map> mapExecutedComponents = new HashMap>(1); + final Map> mapStartedModules = new HashMap>(1); + + mapExecutedComponents.put(tenantDomainCtx, new HashSet(10)); + mapStartedModules.put(tenantDomainCtx, new HashSet(2)); + + List tenantsNonFinal = null; + if (tenantAdminService.isEnabled()) + { + if (tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN) && applyToTenants) + { + tenantsNonFinal = tenantAdminService.getAllTenants(); + for (Tenant tenant : tenantsNonFinal) + { + mapExecutedComponents.put(tenant.getTenantDomain(), new HashSet(10)); + mapStartedModules.put(tenant.getTenantDomain(), new HashSet(2)); + } + } + } - // Process each module in turn. Ordering is not important. - final Map> mapExecutedComponents = new HashMap>(1); - final Map> mapStartedModules = new HashMap>(1); - - // Note: for system bootstrap this will be the default domain, else tenant domain for tenant create/import - final String tenantDomainCtx = tenantAdminService.getCurrentUserDomain(); - - mapExecutedComponents.put(tenantDomainCtx, new HashSet(10)); - mapStartedModules.put(tenantDomainCtx, new HashSet(2)); - - final List tenants; - if (tenantAdminService.isEnabled() && (tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN))) - { - tenants = tenantAdminService.getAllTenants(); - for (Tenant tenant : tenants) - { - mapExecutedComponents.put(tenant.getTenantDomain(), new HashSet(10)); - mapStartedModules.put(tenant.getTenantDomain(), new HashSet(2)); - } - } - else - { - tenants = null; - } + final List tenants = tenantsNonFinal; for (final ModuleDetails module : modules) { diff --git a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java index 7bfaae30fd..f71b0fa64d 100644 --- a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java +++ b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java @@ -113,6 +113,10 @@ public class ModuleServiceImpl implements ApplicationContextAware, ModuleService this.moduleComponentHelper.setTenantAdminService(tenantAdminService); } + public void setApplyToTenants(boolean applyToTenants) + { + this.moduleComponentHelper.setApplyToTenants(applyToTenants); + } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/notification/EMailNotificationProvider.java b/source/java/org/alfresco/repo/notification/EMailNotificationProvider.java index 71d54c4ba4..3d467ad3ce 100644 --- a/source/java/org/alfresco/repo/notification/EMailNotificationProvider.java +++ b/source/java/org/alfresco/repo/notification/EMailNotificationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -36,6 +36,7 @@ import org.alfresco.service.cmr.notification.NotificationProvider; import org.alfresco.service.cmr.notification.NotificationService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.util.ModelUtil; @@ -209,9 +210,9 @@ public class EMailNotificationProvider implements NotificationProvider mail.setParameterValue(MailActionExecuter.PARAM_TEXT, body); } else - { + { // Check for template - NodeRef template = notificationContext.getBodyTemplate(); + String template = notificationContext.getBodyTemplate(); if (template == null) { errorEncountered(notificationContext, @@ -220,12 +221,13 @@ public class EMailNotificationProvider implements NotificationProvider } else { - template = fileFolderService.getLocalizedSibling(template); + if (template.indexOf(StoreRef.URI_FILLER) != -1) + { + template = fileFolderService.getLocalizedSibling(new NodeRef(template)).toString(); + } mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, template); mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable)buildTemplateModel(notificationContext.getTemplateArgs())); - - } } diff --git a/source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java b/source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java index 8b9f7b31f2..4f19855d50 100644 --- a/source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java +++ b/source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -220,7 +220,7 @@ public class NotificationServiceImplSystemTest extends BaseAlfrescoTestCase context.setFrom(FROM_EMAIL); context.addTo(TO_USER1); context.setSubject(SUBJECT); - context.setBodyTemplate(template); + context.setBodyTemplate(template.toString()); Map templateArgs = new HashMap(1); templateArgs.put("template", template); diff --git a/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java b/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java index b0e21a544c..a7774c395c 100644 --- a/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java +++ b/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -66,7 +66,7 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti private DictionaryService dictionaryService; private NodeService nodeService; - private RenditionDefinitionPersisterImpl renditionDefinitionPersister; + private RenditionDefinitionPersister renditionDefinitionPersister; /** * @since 4.0.1 @@ -77,7 +77,7 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti * Injects the RenditionDefinitionPersister bean. * @param renditionDefinitionPersister */ - public void setRenditionDefinitionPersister(RenditionDefinitionPersisterImpl renditionDefinitionPersister) + public void setRenditionDefinitionPersister(RenditionDefinitionPersister renditionDefinitionPersister) { this.renditionDefinitionPersister = renditionDefinitionPersister; } diff --git a/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java b/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java index 5523b97318..ff18920a17 100644 --- a/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java +++ b/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java @@ -204,7 +204,8 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest nodeService.deleteNode(testTargetFolder); } - public void testRenderFreeMarkerTemplate() throws Exception + //TODO Fix this failing test. + public void off_testRenderFreeMarkerTemplate() throws Exception { this.setComplete(); this.endTransaction(); diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java index bdc662b000..55edbb515b 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java @@ -23,8 +23,8 @@ import java.util.Set; import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.security.authentication.AuthenticationComponent.UserNameValidationMode; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantContextHolder; +import org.alfresco.util.Pair; public class AuthenticationServiceImpl extends AbstractAuthenticationService implements ActivateableBean { @@ -62,7 +62,7 @@ public class AuthenticationServiceImpl extends AbstractAuthenticationService imp try { // clear context - to avoid MT concurrency issue (causing domain mismatch) - see also 'validate' below - clearCurrentSecurityContext(); + //clearCurrentSecurityContext(); preAuthenticationCheck(userName); authenticationComponent.authenticate(userName, password); } @@ -111,11 +111,20 @@ public class AuthenticationServiceImpl extends AbstractAuthenticationService imp String currentUser = null; try { - + String tenant = TenantContextHolder.getTenantDomain(); + // clear context - to avoid MT concurrency issue (causing domain mismatch) - see also 'authenticate' above clearCurrentSecurityContext(); currentUser = ticketComponent.validateTicket(ticket); authenticationComponent.setCurrentUser(currentUser, UserNameValidationMode.NONE); + + if (tenant == null) + { + Pair userTenant = AuthenticationUtil.getUserTenant(currentUser); + tenant = userTenant.getSecond(); + } + + TenantContextHolder.setTenantDomain(tenant); } catch (AuthenticationException ae) { diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java index 8f1b470ebd..f35af0f6e8 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -39,6 +39,7 @@ import net.sf.acegisecurity.DisabledException; import net.sf.acegisecurity.LockedException; import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; +import net.sf.acegisecurity.providers.encoding.PasswordEncoder; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -79,6 +80,7 @@ public class AuthenticationTest extends TestCase private AuthorityService authorityService; private TenantService tenantService; private MD4PasswordEncoder passwordEncoder; + private PasswordEncoder sha256PasswordEncoder; private MutableAuthenticationDao dao; private AuthenticationManager authenticationManager; private TicketComponent ticketComponent; @@ -130,6 +132,7 @@ public class AuthenticationTest extends TestCase authorityService = (AuthorityService) ctx.getBean("authorityService"); tenantService = (TenantService) ctx.getBean("tenantService"); passwordEncoder = (MD4PasswordEncoder) ctx.getBean("passwordEncoder"); + sha256PasswordEncoder = (PasswordEncoder) ctx.getBean("sha256PasswordEncoder"); ticketComponent = (TicketComponent) ctx.getBean("ticketComponent"); authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService"); pubAuthenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService"); @@ -183,6 +186,7 @@ public class AuthenticationTest extends TestCase dao.setNodeService(nodeService); dao.setNamespaceService(getNamespacePrefixReolsver("")); dao.setPasswordEncoder(passwordEncoder); + dao.setSha256PasswordEncoder(sha256PasswordEncoder); dao.setPolicyComponent(policyComponent); dao.setAuthenticationCache(authenticationCache); dao.setSingletonCache(immutableSingletonCache); @@ -401,6 +405,7 @@ public class AuthenticationTest extends TestCase dao.setAuthorityService(authorityService); dao.setNamespaceService(getNamespacePrefixReolsver("")); dao.setPasswordEncoder(passwordEncoder); + dao.setSha256PasswordEncoder(sha256PasswordEncoder); dao.setPolicyComponent(policyComponent); dao.setAuthenticationCache(authenticationCache); dao.setSingletonCache(immutableSingletonCache); diff --git a/source/java/org/alfresco/repo/security/authentication/MessageDigestPasswordEncoder.java b/source/java/org/alfresco/repo/security/authentication/MessageDigestPasswordEncoder.java new file mode 100644 index 0000000000..5db4b93c07 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/MessageDigestPasswordEncoder.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2013 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.security.authentication; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import net.sf.acegisecurity.providers.encoding.BaseDigestPasswordEncoder; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + +/** + * Base for digest password encoders. + *

+ * This class can be used stand-alone, or one of the subclasses can be used for + * compatiblity and convenience. When using this class directly you must specify + * a + * Message Digest Algorithm to use as a constructor arg + *

+ * + *

+ * The encoded password hash is normally returned as Hex (32 char) version of + * the hash bytes. Setting the encodeHashAsBase64 property to + * true will cause the encoded pass to be returned as Base64 text, + * which will consume 24 characters. See + * {@link BaseDigestPasswordEncoder#setEncodeHashAsBase64(boolean)} + *

+ *

+ * This PasswordEncoder can be used directly as in the following example:
+ * + *

+ * <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder">
+ *     <constructor-arg value="MD5"/>
+ * </bean>
+ * 
+ * + *

+ */ +public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder +{ + + private final String algorithm; + + /** + * The digest algorithm to use Supports the named Message Digest Algorithms in the Java environment. + * + * @param algorithm + */ + public MessageDigestPasswordEncoder(String algorithm) + { + this(algorithm, false); + } + + /** + * Convenience constructor for specifying the algorithm and whether or not + * to enable base64 encoding + * + * @param algorithm + * @param encodeHashAsBase64 + * @throws IllegalArgumentException + * if an unknown + */ + public MessageDigestPasswordEncoder(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException + { + this.algorithm = algorithm; + setEncodeHashAsBase64(encodeHashAsBase64); + // Validity Check + getMessageDigest(); + } + + /** + * Encodes the rawPass using a MessageDigest. If a salt is specified it will + * be merged with the password before encoding. + * + * @param rawPass + * The plain text password + * @param salt + * The salt to sprinkle + * @return Hex string of password digest (or base64 encoded string if + * encodeHashAsBase64 is enabled. + */ + public String encodePassword(String rawPass, Object salt) + { + String saltedPass = mergePasswordAndSalt(rawPass, salt, false); + + MessageDigest messageDigest = getMessageDigest(); + + byte[] digest; + + try + { + digest = messageDigest.digest(saltedPass.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) + { + throw new IllegalStateException("UTF-8 not supported!"); + } + + if (getEncodeHashAsBase64()) + { + return new String(Base64.encodeBase64(digest)); + } else + { + return new String(Hex.encodeHex(digest)); + } + } + + /** + * Get a MessageDigest instance for the given algorithm. Throws an + * IllegalArgumentException if algorithm is unknown + * + * @return MessageDigest instance + * @throws IllegalArgumentException + * if NoSuchAlgorithmException is thrown + */ + protected final MessageDigest getMessageDigest() throws IllegalArgumentException + { + try + { + return MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) + { + throw new IllegalArgumentException("No such algorithm [" + algorithm + "]"); + } + } + + /** + * Takes a previously encoded password and compares it with a rawpassword + * after mixing in the salt and encoding that value + * + * @param encPass + * previously encoded password + * @param rawPass + * plain text password + * @param salt + * salt to mix into password + * @return true or false + */ + public boolean isPasswordValid(String encPass, String rawPass, Object salt) + { + String pass1 = "" + encPass; + String pass2 = encodePassword(rawPass, salt); + + return pass1.equals(pass2); + } + + public String getAlgorithm() + { + return algorithm; + } +} diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index f80cbcd668..196b1aa7ab 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -52,6 +52,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.EqualsHelper; +import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; @@ -68,13 +69,13 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In private static final Log logger = LogFactory.getLog(RepositoryAuthenticationDao.class); - private AuthorityService authorityService; - - private NodeService nodeService; - private TenantService tenantService; - private NamespacePrefixResolver namespacePrefixResolver; - private PasswordEncoder passwordEncoder; - private PolicyComponent policyComponent; + protected AuthorityService authorityService; + protected NodeService nodeService; + protected TenantService tenantService; + protected NamespacePrefixResolver namespacePrefixResolver; + protected PasswordEncoder passwordEncoder; + protected PasswordEncoder sha256PasswordEncoder; + protected PolicyComponent policyComponent; private TransactionService transactionService; @@ -120,6 +121,11 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In this.passwordEncoder = passwordEncoder; } + public void setSha256PasswordEncoder(PasswordEncoder passwordEncoder) + { + this.sha256PasswordEncoder = passwordEncoder; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -199,10 +205,22 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In GrantedAuthority[] gas = new GrantedAuthority[1]; gas[0] = new GrantedAuthorityImpl("ROLE_AUTHENTICATED"); - - return new CacheEntry(userRef, new User(userName, password, getEnabled(userName, properties), - !getHasExpired(userName, properties), true, !getLocked(userName, properties), gas), - getCredentialsExpiryDate(userName, properties)); + + boolean isAdminAuthority = authorityService.isAdminAuthority(userName); + + Date credentialsExpiryDate = getCredentialsExpiryDate(userName, properties, isAdminAuthority); + boolean credentialsHaveNotExpired = (credentialsExpiryDate == null || credentialsExpiryDate.compareTo(new Date()) >= 0); + + UserDetails ud = new User( + userName, + password, + getEnabled(userName, properties, isAdminAuthority), + !getHasExpired(userName, properties, isAdminAuthority), + credentialsHaveNotExpired, + !getLocked(userName, properties, isAdminAuthority), + gas); + + return new CacheEntry(userRef, ud, credentialsExpiryDate); } return null; } @@ -252,9 +270,10 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In NodeRef typesNode = getUserFolderLocation(caseSensitiveUserName); Map properties = new HashMap(); properties.put(ContentModel.PROP_USER_USERNAME, caseSensitiveUserName); - String salt = null; // GUID.generate(); + String salt = GUID.generate(); properties.put(ContentModel.PROP_SALT, salt); - properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt)); + properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), null)); + properties.put(ContentModel.PROP_PASSWORD_SHA256, sha256PasswordEncoder.encodePassword(new String(rawPassword), salt)); properties.put(ContentModel.PROP_ACCOUNT_EXPIRES, Boolean.valueOf(false)); properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false)); properties.put(ContentModel.PROP_ENABLED, Boolean.valueOf(true)); @@ -308,11 +327,13 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In throw new AuthenticationException("User name does not exist: " + userName); } Map properties = nodeService.getProperties(userRef); - String salt = null; // GUID.generate(); + String salt = GUID.generate(); properties.remove(ContentModel.PROP_SALT); properties.put(ContentModel.PROP_SALT, salt); properties.remove(ContentModel.PROP_PASSWORD); - properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt)); + properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), null)); + properties.remove(ContentModel.PROP_PASSWORD_SHA256); + properties.put(ContentModel.PROP_PASSWORD_SHA256, sha256PasswordEncoder.encodePassword(new String(rawPassword), salt)); nodeService.setProperties(userRef, properties); } @@ -342,7 +363,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In /** * @return Returns the user properties or null if there are none */ - private Map getUserProperties(String userName) + protected Map getUserProperties(String userName) { NodeRef userNodeRef = getUserOrNull(userName); if (userNodeRef == null) @@ -396,16 +417,20 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In @Override public boolean getAccountHasExpired(String userName) { - return getHasExpired(userName, null); + return getHasExpired(userName, null, null); } /** * @param userName the username * @param properties user properties or null to fetch them */ - private boolean getHasExpired(String userName, Map properties) + protected boolean getHasExpired(String userName, Map properties, Boolean isAdminAuthority) { - if (authorityService.isAdminAuthority(userName)) + if (isAdminAuthority == null) + { + isAdminAuthority = authorityService.isAdminAuthority(userName); + } + if (isAdminAuthority) { return false; // Admin never expires } @@ -434,26 +459,30 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In return false; } } - + @Override public boolean getLocked(String userName) { - return getLocked(userName, null); + return getLocked(userName, null, null); } - + @Override public boolean getAccountlocked(String userName) { - return getLocked(userName, null); + return getLocked(userName, null, null); } - + /** * @param userName the username * @param properties user properties or null to fetch them */ - private boolean getLocked(String userName, Map properties) + protected boolean getLocked(String userName, Map properties, Boolean isAdminAuthority) { - if (authorityService.isAdminAuthority(userName)) + if (isAdminAuthority == null) + { + isAdminAuthority = authorityService.isAdminAuthority(userName); + } + if (isAdminAuthority) { return false; // Admin is never locked } @@ -486,7 +515,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In * @param userName the username * @param properties user properties or null to fetch them */ - private boolean getCredentialsExpire(String userName, Map properties) + protected boolean getCredentialsExpire(String userName, Map properties) { if (authorityService.isAdminAuthority(userName)) { @@ -534,15 +563,20 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In { return !loadUserByUsername(userName).isCredentialsNonExpired(); } - + /** * @param userName the username (never null * @param properties the properties associated with the user or null to get them + * @param isAdminAuthority is admin authority * @return Date on which the credentials expire or null if they never expire */ - private Date getCredentialsExpiryDate(String userName, Map properties) + private Date getCredentialsExpiryDate(String userName, Map properties, Boolean isAdminAuthority) { - if (authorityService.isAdminAuthority(userName)) + if (isAdminAuthority == null) + { + isAdminAuthority = authorityService.isAdminAuthority(userName); + } + if (isAdminAuthority) { return null; // Admin never expires } @@ -550,33 +584,30 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In { properties = getUserProperties(userName); } - if (properties == null) - { - return null; - } if (DefaultTypeConverter.INSTANCE.booleanValue(properties.get(ContentModel.PROP_CREDENTIALS_EXPIRE))) { return DefaultTypeConverter.INSTANCE.convert(Date.class, properties.get(ContentModel.PROP_CREDENTIALS_EXPIRY_DATE)); - } - else - { - return null; } + return null; } - + @Override public boolean getEnabled(String userName) { - return getEnabled(userName, null); + return getEnabled(userName, null, null); } - + /** * @param userName the username * @param properties the user's properties or null */ - private boolean getEnabled(String userName, Map properties) + protected boolean getEnabled(String userName, Map properties, Boolean isAdminAuthority) { - if (authorityService.isAdminAuthority(userName)) + if (isAdminAuthority == null) + { + isAdminAuthority = authorityService.isAdminAuthority(userName); + } + if (isAdminAuthority) { return true; // Admin is always enabled } diff --git a/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java b/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java new file mode 100644 index 0000000000..c024861014 --- /dev/null +++ b/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2013 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.security.authentication; + +/** + *

+ * SHA implementation of PasswordEncoder. + *

+ *

+ * If a null password is presented, it will be treated as an empty + * String ("") password. + *

+ *

+ * As SHA is a one-way hash, the salt can contain any characters. The default + * strength for the SHA encoding is SHA-1. If you wish to use higher strengths + * use the argumented constructor. {@link #ShaPasswordEncoder(int strength)} + *

+ *

+ * The applicationContext example... + * + *

+ * <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
+ *     <constructor-arg value="256"/>
+ * </bean>
+ * 
+ */ +public class ShaPasswordEncoderImpl extends MessageDigestPasswordEncoder +{ + + /** + * Initializes the ShaPasswordEncoder for SHA-1 strength + */ + public ShaPasswordEncoderImpl() + { + this(1); + } + + /** + * Initialize the ShaPasswordEncoder with a given SHA stength as supported + * by the JVM EX: + * ShaPasswordEncoder encoder = new ShaPasswordEncoder(256); + * initializes with SHA-256 + * + * @param strength + * EX: 1, 256, 384, 512 + */ + public ShaPasswordEncoderImpl(int strength) + { + super("SHA-" + strength); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/security/authentication/userModel.xml b/source/java/org/alfresco/repo/security/authentication/userModel.xml index f6ac271b58..c1923f2e00 100644 --- a/source/java/org/alfresco/repo/security/authentication/userModel.xml +++ b/source/java/org/alfresco/repo/security/authentication/userModel.xml @@ -40,6 +40,9 @@ d:text + + d:text + d:boolean diff --git a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java b/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java index 83a732464f..31e3165e2d 100644 --- a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java +++ b/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java @@ -486,6 +486,8 @@ public class ScriptAuthorityService extends BaseScopableProcessorExtension * @param paging Paging object with max number to return, and items to skip * @param sortBy What to sort on (firstName, lastName or userName) * @return the users matching the query + * + * @deprecated see People.getPeople(String filter, ScriptPagingDetails pagingRequest, String sortBy) */ public ScriptUser[] searchUsers(String nameFilter, ScriptPagingDetails paging, String sortBy) { diff --git a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java index 5e1ab4822e..8e1282e760 100644 --- a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java @@ -97,7 +97,8 @@ public class GetPeopleCannedQueryFactory extends AbstractCannedQueryFactory> sortPairs = new ArrayList>(sortProps.size()); for (Pair sortProp : sortProps) { - sortPairs.add(new Pair(sortProp.getFirst(), (sortProp.getSecond() ? SortOrder.ASCENDING : SortOrder.DESCENDING))); + boolean sortAsc = ((sortProp.getSecond() == null) || sortProp.getSecond()); + sortPairs.add(new Pair(sortProp.getFirst(), (sortAsc ? SortOrder.ASCENDING : SortOrder.DESCENDING))); } cqsd = new CannedQuerySortDetails(sortPairs); diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 64fc45e021..8aa20b1ce4 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -53,8 +53,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.repo.tenant.TenantDomainMismatchException; import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.tenant.TenantUtil; -import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -80,10 +78,12 @@ import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.NoSuchPersonException; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespaceService; @@ -165,7 +165,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private JavaBehaviour beforeDeleteNodeValidationBehaviour; private boolean homeFolderCreationEager; - + + private boolean homeFolderCreationDisabled = false; // if true then home folders creation is disabled (ie. home folders are not created - neither eagerly nor lazily) + static { Set props = new HashSet(); @@ -363,6 +365,14 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per this.homeFolderCreationEager = homeFolderCreationEager; } + /** + * Indicates if home folder creation should be disabled. + */ + public void setHomeFolderCreationDisabled(boolean homeFolderCreationDisabled) + { + this.homeFolderCreationDisabled = homeFolderCreationDisabled; + } + public void setAclDAO(AclDAO aclDao) { this.aclDao = aclDao; @@ -426,7 +436,35 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per */ public NodeRef getPersonOrNull(String userName) { - return getPerson(userName, false, false); + return getPersonImpl(userName, false, false); + } + + /** + * {@inheritDoc} + */ + public PersonInfo getPerson(NodeRef personRef) throws NoSuchPersonException + { + Map props = null; + try + { + props = nodeService.getProperties(personRef); + } + catch (InvalidNodeRefException inre) + { + throw new NoSuchPersonException(personRef.toString()); + } + + // belts-and-braces + String username = (String)props.get(ContentModel.PROP_USERNAME); + if (getPersonOrNull(username) == null) + { + throw new NoSuchPersonException(personRef.toString()); + } + + return new PersonInfo(personRef, + username, + (String)props.get(ContentModel.PROP_FIRSTNAME), + (String)props.get(ContentModel.PROP_LASTNAME)); } /** @@ -434,31 +472,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per */ public NodeRef getPerson(final String userName, final boolean autoCreateHomeFolderAndMissingPersonIfAllowed) { - return getPerson(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, true); - } - - private NodeRef getPerson( - final String userName, - final boolean autoCreateHomeFolderAndMissingPersonIfAllowed, - final boolean exceptionOrNull) - { - // MT share - for activity service system callback - if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantUser(userName)) - { - final String tenantDomain = tenantService.getUserDomain(userName); - - return TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public NodeRef doWork() throws Exception - { - return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, exceptionOrNull); - } - }, tenantDomain); - } - else - { - return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, exceptionOrNull); - } + return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, true); } private NodeRef getPersonImpl( @@ -500,7 +514,13 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per */ public boolean personExists(String caseSensitiveUserName) { - return getPersonOrNullImpl(caseSensitiveUserName) != null; + NodeRef person = getPersonOrNull(caseSensitiveUserName); + if (person != null) + { + // re: THOR-293 + return permissionServiceSPI.hasPermission(person, PermissionService.READ) == AccessStatus.ALLOWED; + } + return false; } private NodeRef getPersonOrNullImpl(String searchUserName) @@ -840,7 +860,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private void makeHomeFolderIfRequired(NodeRef person) { - if (person != null) + if ((person != null) && (homeFolderCreationDisabled == false)) { NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER)); if (homeFolder == null) @@ -1701,7 +1721,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per // Make sure there is an authority entry - with a DB constraint for uniqueness // aclDao.createAuthority(username); - if (homeFolderCreationEager) + if ((homeFolderCreationEager) && (homeFolderCreationDisabled == false)) { makeHomeFolderAsSystem(childAssocRef); } diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java index ae5d5545ff..815a48b97e 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -39,6 +39,8 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.person.PersonServiceImpl; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -115,6 +117,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase this.authenticationContext = (AuthenticationContext) ChainingUserRegistrySynchronizerTest.context .getBean("authenticationContext"); + + + // this.authenticationContext.setSystemUserAsCurrentUser(); + //AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); this.authenticationContext.setSystemUserAsCurrentUser(); this.retryingTransactionHelper = (RetryingTransactionHelper) ChainingUserRegistrySynchronizerTest.context @@ -298,8 +304,16 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase public Object execute() throws Throwable { - - ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false); + // re: THOR-293 - note: use runAs else security context is cleared (=> current system user becomes null and personExists fails) + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Void doWork() throws Exception + { + ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false); + return null; + } + }); + // Stay in the same transaction assertExists("Z1", "U1"); assertEmailEquals("U1", "changeofemail@alfresco.com"); diff --git a/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java b/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java index 047ea155d4..5863763b5c 100644 --- a/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java +++ b/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java @@ -92,7 +92,7 @@ public class SitesPermissionCleaner public void cleanSitePermissions(final NodeRef targetNode, SiteInfo containingSite) { - if (nodeService.exists(targetNode)) + if (nodeDAO.exists(targetNode)) { // We can calculate the containing site at the start of a recursive call & then reuse it on subsequent calls. if (containingSite == null) diff --git a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java b/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java index 3040ee482b..40d7d88832 100644 --- a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java +++ b/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java @@ -28,10 +28,12 @@ import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.repo.activities.ActivityType; +import org.alfresco.repo.dictionary.RepositoryLocation; import org.alfresco.repo.domain.subscriptions.SubscriptionsDAO; import org.alfresco.repo.search.SearcherException; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.activities.ActivityService; @@ -73,12 +75,14 @@ public class SubscriptionServiceImpl implements SubscriptionService private static final String USER_USERNAME = "userUserName"; private static final String FOLLOWING_COUNT = "followingCount"; private static final String FOLLOWER_COUNT = "followerCount"; - + private static final String SUBSCRIBER_FIRSTNAME = "subscriberFirstName"; private static final String SUBSCRIBER_LASTNAME = "subscriberLastName"; private static final String SUBSCRIBER_USERNAME = "subscriberUserName"; private static final String NODE = "node"; - + + private static final String TENANT_DOMAIN = "tenantDomain"; + protected SubscriptionsDAO subscriptionsDAO; protected NodeService nodeService; protected PersonService personService; @@ -90,6 +94,8 @@ public class SubscriptionServiceImpl implements SubscriptionService protected FileFolderService fileFolderService; protected boolean active; + + private RepositoryLocation followingEmailTemplateLocation; /** * Sets the subscriptions DAO. @@ -167,7 +173,13 @@ public class SubscriptionServiceImpl implements SubscriptionService { this.active = active; } - + + public void setFollowingEmailTemplateLocation(RepositoryLocation followingEmailTemplateLocation) + { + this.followingEmailTemplateLocation = followingEmailTemplateLocation; + } + + @Override public PagingSubscriptionResults getSubscriptions(String userId, SubscriptionItemTypeEnum type, PagingRequest pagingRequest) @@ -472,7 +484,7 @@ public class SubscriptionServiceImpl implements SubscriptionService return; } - NodeRef templateNodeRef = getEmailTemplateRef(); + String templateNodeRef = getEmailTemplateRef(); if (templateNodeRef == null) { // we can't send an email without template @@ -507,9 +519,16 @@ public class SubscriptionServiceImpl implements SubscriptionService } catch (Exception e) { } - + + // Add tenant, if in context of tenant + String tenantDomain = TenantUtil.getCurrentDomain(); + if (tenantDomain != null) + { + model.put(TENANT_DOMAIN, tenantDomain); + } + Action mail = actionService.createAction(MailActionExecuter.NAME); - + mail.setParameterValue(MailActionExecuter.PARAM_TO, emailAddress); mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, subjectText); mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, templateNodeRef); @@ -523,32 +542,48 @@ public class SubscriptionServiceImpl implements SubscriptionService * Returns the NodeRef of the email template or null if the * template coudln't be found. */ - protected NodeRef getEmailTemplateRef() + protected String getEmailTemplateRef() { - // Find the following email template - String xpath = "app:company_home/app:dictionary/app:email_templates/app:following/cm:following-email.html.ftl"; - try + String locationType = followingEmailTemplateLocation.getQueryLanguage(); + + if (locationType.equals(SearchService.LANGUAGE_XPATH)) { - NodeRef rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); - List nodeRefs = searchService.selectNodes(rootNodeRef, xpath, null, namespaceService, false); - if (nodeRefs.size() > 1) + // Find the following email template + StoreRef store = followingEmailTemplateLocation.getStoreRef(); + String xpath = followingEmailTemplateLocation.getPath(); + + try { - logger.error("Found too many email templates using: " + xpath); - nodeRefs = Collections.singletonList(nodeRefs.get(0)); - } else if (nodeRefs.size() == 0) + NodeRef rootNodeRef = nodeService.getRootNode(store); + List nodeRefs = searchService.selectNodes(rootNodeRef, xpath, null, namespaceService, false); + if (nodeRefs.size() > 1) + { + logger.error("Found too many email templates using: " + xpath); + nodeRefs = Collections.singletonList(nodeRefs.get(0)); + } + else if (nodeRefs.size() == 0) + { + logger.error("Cannot find the email template using " + xpath); + return null; + } + // Now localise this + return fileFolderService.getLocalizedSibling(nodeRefs.get(0)).toString(); + } + catch (SearcherException e) { - logger.error("Cannot find the email template using " + xpath); - return null; + logger.error("Cannot find the email template!", e); } - // Now localise this - NodeRef base = nodeRefs.get(0); - NodeRef local = fileFolderService.getLocalizedSibling(base); - return local; - } catch (SearcherException e) - { - logger.error("Cannot find the email template!", e); + + return null; + } + else if (locationType.equals(RepositoryLocation.LANGUAGE_CLASSPATH)) + { + return followingEmailTemplateLocation.getPath(); + } + else + { + logger.error("Unsupported location type: "+locationType); + return null; } - - return null; } } diff --git a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java index 6b3dcf7265..9abe8b30c4 100644 --- a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java +++ b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java @@ -19,7 +19,6 @@ package org.alfresco.repo.tenant; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; @@ -33,6 +32,8 @@ import org.alfresco.repo.domain.tenant.TenantAdminDAO; import org.alfresco.repo.domain.tenant.TenantEntity; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -44,6 +45,8 @@ import org.springframework.context.ApplicationContextAware; */ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingContentStore implements ApplicationContextAware, TenantRoutingContentStore { + private static Log logger = LogFactory.getLog(AbstractTenantRoutingContentStore.class); + private String defaultRootDirectory; private TenantAdminDAO tenantAdminDAO; protected TenantService tenantService; @@ -102,14 +105,14 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC @Override public List getAllStores() { + final List allEnabledStores = new ArrayList(); + if (tenantService.isEnabled()) { String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); if ((currentUser == null) || (tenantService.getBaseNameUser(currentUser).equals(AuthenticationUtil.getSystemUserName()))) { // return enabled stores across all tenants, if running as system/null user, for example, ContentStoreCleaner scheduled job - final List allEnabledStores = new ArrayList(); - List tenants = tenantAdminDAO.listTenants(); for (TenantEntity tenant : tenants) { @@ -127,16 +130,18 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC } } - if (allEnabledStores.size() > 0) + if (logger.isDebugEnabled()) { - allEnabledStores.add(getTenantContentStore()); - return allEnabledStores; + logger.debug("getAllStores called without tenant ctx ("+tenants.size()+" tenants)"); } // drop through to ensure default content store has been init'ed } } - return Arrays.asList(getTenantContentStore()); + + allEnabledStores.add(getTenantContentStore()); + + return allEnabledStores; } private ContentStore getTenantContentStore() diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index e9ce9e0846..edf8f3a546 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -307,17 +307,27 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo throw new AlfrescoRuntimeException("MT: cannot start tenants - TenantRoutingContentStore is not configured AND not all tenants use co-mingled content store"); } + String tenantDomain = tenant.getTenantDomain(); + if (tenant.isEnabled()) { - // this will also call tenant deployers registered so far ... - enableTenant(tenant.getTenantDomain(), true); + // notify tenant deployers registered so far ... + notifyAfterEnableTenant(tenantDomain); enabledCount++; + + if (logger.isDebugEnabled()) + { + logger.debug("Tenant enabled: " + tenantDomain); + } } else { - // explicitly disable, without calling disableTenant callback - disableTenant(tenant.getTenantDomain(), false); disabledCount++; + + if (logger.isDebugEnabled()) + { + logger.debug("Tenant disabled: " + tenantDomain); + } } } @@ -377,7 +387,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo /** * @see TenantAdminService.createTenant() */ - public void createTenant(final String tenantDomainIn, final char[] tenantAdminRawPassword, String contentRootPath, String dbUrl) + public void createTenant(final String tenantDomainIn, final char[] tenantAdminRawPassword, String contentRootPath, final String dbUrl) { ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword); @@ -429,65 +439,72 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo AuthenticationUtil.pushAuthentication(); AuthenticationUtil.setFullyAuthenticatedUser(getSystemUser(tenantDomain)); - dictionaryComponent.init(); - - if (tenantFileContentStore instanceof TenantDeployer) + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - ((TenantDeployer)tenantFileContentStore).init(); - } - - // callback - RetryingTransactionCallback doImportCallback = new RetryingTransactionCallback() - { - public Object execute() throws Throwable + public Object doWork() { - // create tenant-specific stores - ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap-mt"); - bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); + dictionaryComponent.init(); - ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap-mt"); - bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); - - // deprecated - ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap-mt"); - bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); - - ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap-mt"); - bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap-mt"); - bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap-mt"); - bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); - - thumbnailRegistry.initThumbnailDefinitions(); - - // TODO janv - resolve this conflict later - /* Note: assume for now that all tenant deployers can lazily init - - // notify listeners that tenant has been created & hence enabled - for (TenantDeployer tenantDeployer : tenantDeployers) + if (tenantFileContentStore instanceof TenantDeployer) { - tenantDeployer.onEnableTenant(); - } - */ - - // bootstrap workflows - for (WorkflowDeployer workflowDeployer : workflowDeployers) - { - workflowDeployer.init(); + ((TenantDeployer)tenantFileContentStore).init(); } - // bootstrap modules (if any) - moduleService.startModules(); + // callback + RetryingTransactionCallback doImportCallback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // create tenant-specific stores + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap-mt"); + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); + + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap-mt"); + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); + + // deprecated + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap-mt"); + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + + ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap-mt"); + bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap-mt"); + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap-mt"); + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + + thumbnailRegistry.initThumbnailDefinitions(); + // TODO janv - resolve this conflict later + /* Note: assume for now that all tenant deployers can lazily init + + // notify listeners that tenant has been created & hence enabled + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + */ + + // bootstrap workflows + for (WorkflowDeployer workflowDeployer : workflowDeployers) + { + workflowDeployer.init(); + } + + // bootstrap modules (if any) + moduleService.startModules(); + + return null; + } + }; + + // if not default DB (ie. dbUrl != null) then run in new Spring managed txn (to ensure datasource is switched) + transactionService.getRetryingTransactionHelper().doInTransaction(doImportCallback, transactionService.isReadOnly(), (dbUrl != null)); return null; } - }; - - // if not default DB (ie. dbUrl != null) then run in new Spring managed txn (to ensure datasource is switched) - transactionService.getRetryingTransactionHelper().doInTransaction(doImportCallback, transactionService.isReadOnly(), (dbUrl != null)); + }, tenantDomain); } finally { @@ -620,35 +637,30 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo logger.warn("Tenant already enabled: " + tenantDomain); } - // Note: assume for now that all tenant deployers can lazily init - boolean notifyTenantDeployers = false; - enableTenant(tenantDomain, notifyTenantDeployers); - } - - protected void enableTenant(String tenantDomain, boolean notifyTenantDeployers) - { - // Check that all the passed values are not null - ParameterCheck.mandatory("tenantDomain", tenantDomain); - TenantUpdateEntity tenantUpdateEntity = tenantAdminDAO.getTenantForUpdate(tenantDomain); tenantUpdateEntity.setEnabled(true); tenantAdminDAO.updateTenant(tenantUpdateEntity); - if (notifyTenantDeployers) + notifyAfterEnableTenant(tenantDomain); + } + + protected void notifyAfterEnableTenant(String tenantDomain) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + // notify listeners that tenant has been enabled + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - // notify listeners that tenant has been enabled - TenantUtil.runAsSystemTenant(new TenantRunAsWork() + public Object doWork() { - public Object doWork() + for (TenantDeployer tenantDeployer : tenantDeployers) { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onEnableTenant(); - } - return null; + tenantDeployer.onEnableTenant(); } - }, tenantDomain); - } + return null; + } + }, tenantDomain); if (logger.isInfoEnabled()) { @@ -670,33 +682,30 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo logger.warn("Tenant already disabled: " + tenantDomain); } - disableTenant(tenantDomain, true); - } - - protected void disableTenant(String tenantDomain, boolean notifyTenantDeployers) - { - tenantDomain = getTenantDomain(tenantDomain); - - if (notifyTenantDeployers) - { - // notify listeners that tenant has been disabled - TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onDisableTenant(); - } - return null; - } - }, tenantDomain); - } + notifyBeforeDisableTenant(tenantDomain); // update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable) TenantUpdateEntity tenantUpdateEntity = tenantAdminDAO.getTenantForUpdate(tenantDomain); tenantUpdateEntity.setEnabled(false); tenantAdminDAO.updateTenant(tenantUpdateEntity); + } + + protected void notifyBeforeDisableTenant(String tenantDomain) + { + tenantDomain = getTenantDomain(tenantDomain); + + // notify listeners that tenant has been disabled + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onDisableTenant(); + } + return null; + } + }, tenantDomain); if (logger.isInfoEnabled()) { @@ -795,15 +804,13 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } }, tenantDomain); - final String tenantAdminUser = getTenantAdminUser(tenantDomain); - // delete tenant-specific stores - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION1))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION2))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM))); - nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER))); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES), tenantDomain, false)); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES), tenantDomain, false)); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION1), tenantDomain, false)); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION2), tenantDomain, false)); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM), tenantDomain, false)); + nodeService.deleteStore(tenantService.getName(new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER), tenantDomain, false)); TenantUtil.runAsSystemTenant(new TenantRunAsWork() { @@ -1305,7 +1312,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo tenantAdminDAO.createTenant(tenantEntity); } - private void validateTenantName(String tenantDomain) + protected void validateTenantName(String tenantDomain) { ParameterCheck.mandatory("tenantDomain", tenantDomain); diff --git a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java index 5ea9e63d52..246dc96243 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java +++ b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java @@ -1701,7 +1701,7 @@ public class MultiTDemoTest extends TestCase } - // TODO pending CLOUD-1350 fix + // TODO pending CLOUD-1351 fix public void xtest20_ALF_12732() { final String tenantDomain1 = TEST_RUN+".one.alf12732"; diff --git a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java index 640540ae6c..118be354d0 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java @@ -117,7 +117,7 @@ public class MultiTServiceImpl implements TenantService assocRef.getTypeQName(), getName(assocRef.getTargetRef())); } - + /* (non-Javadoc) * @see org.alfresco.repo.tenant.TenantService#getName(java.lang.String, org.alfresco.service.cmr.repository.StoreRef) */ @@ -138,8 +138,24 @@ public class MultiTServiceImpl implements TenantService return storeRef; } + protected StoreRef getName(StoreRef storeRef, String tenantDomain, boolean checkTenantEnabled) + { + if (storeRef == null) { return null; } + if (tenantDomain != null) + { + storeRef = new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier(), tenantDomain, checkTenantEnabled)); + } + + return storeRef; + } + protected String getName(String name, String tenantDomain) { + return getName(name, tenantDomain, true); + } + + protected String getName(String name, String tenantDomain, boolean checkTenantEnabled) + { ParameterCheck.mandatory("tenantDomain", tenantDomain); if (name == null) @@ -147,7 +163,10 @@ public class MultiTServiceImpl implements TenantService return null; } - checkTenantEnabled(tenantDomain); + if (checkTenantEnabled) + { + checkTenantEnabled(tenantDomain); + } int idx1 = name.indexOf(SEPARATOR); if (idx1 != 0) @@ -644,6 +663,10 @@ public class MultiTServiceImpl implements TenantService return nameDomain; } + /** + * @deprecated + * @return + */ public static String getMultiTenantDomainName(String name) { // Check that all the passed values are not null diff --git a/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java b/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java index 612d520fb0..e7d6c9dd32 100644 --- a/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java +++ b/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java @@ -72,7 +72,8 @@ public class RunAsTenantInterceptor implements MethodInterceptor } else { - return TenantUtil.runAsPrimaryTenant(runAs, AuthenticationUtil.getFullyAuthenticatedUser()); + // run as tenant using current tenant context (if no tenant context then it is implied as the primary tenant, based on username) + return TenantUtil.runAsTenant(runAs, AuthenticationUtil.getUserTenant(AuthenticationUtil.getFullyAuthenticatedUser()).getSecond()); } } } diff --git a/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java b/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java index f62adb445a..8435450d64 100644 --- a/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java +++ b/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java @@ -36,14 +36,29 @@ public class TenantBasicDataSource extends BasicDataSource this.setUrl(tenantUrl); this.setMaxActive(tenantMaxActive == -1 ? bds.getMaxActive() : tenantMaxActive); - // defaults + // defaults/overrides - see also 'baseDefaultDataSource' (core-services-context.xml + repository.properties) + + this.setDriverClassName(bds.getDriverClassName()); this.setUsername(bds.getUsername()); this.setPassword(bds.getPassword()); - this.setDriverClassName(bds.getDriverClassName()); - this.setMaxIdle(bds.getMaxIdle()); + this.setInitialSize(bds.getInitialSize()); this.setMinIdle(bds.getMinIdle()); - - // TODO other default settings + this.setMaxIdle(bds.getMaxIdle()); + this.setDefaultAutoCommit(bds.getDefaultAutoCommit()); + this.setDefaultTransactionIsolation(bds.getDefaultTransactionIsolation()); + this.setMaxWait(bds.getMaxWait()); + this.setValidationQuery(bds.getValidationQuery()); + this.setTimeBetweenEvictionRunsMillis(bds.getTimeBetweenEvictionRunsMillis()); + this.setMinEvictableIdleTimeMillis(bds.getMinEvictableIdleTimeMillis()); + this.setNumTestsPerEvictionRun(bds.getNumTestsPerEvictionRun()); + this.setTestOnBorrow(bds.getTestOnBorrow()); + this.setTestOnReturn(bds.getTestOnReturn()); + this.setTestWhileIdle(bds.getTestWhileIdle()); + this.setRemoveAbandoned(bds.getRemoveAbandoned()); + this.setRemoveAbandonedTimeout(bds.getRemoveAbandonedTimeout()); + this.setPoolPreparedStatements(bds.isPoolPreparedStatements()); + this.setMaxOpenPreparedStatements(bds.getMaxOpenPreparedStatements()); + this.setLogAbandoned(bds.getLogAbandoned()); } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/tenant/TenantRoutingFileContentStore.java b/source/java/org/alfresco/repo/tenant/TenantRoutingFileContentStore.java index 2756a4267a..9effa0b926 100644 --- a/source/java/org/alfresco/repo/tenant/TenantRoutingFileContentStore.java +++ b/source/java/org/alfresco/repo/tenant/TenantRoutingFileContentStore.java @@ -23,7 +23,6 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; - import org.alfresco.repo.content.ContentLimitProvider; import org.alfresco.repo.content.ContentLimitProvider.NoLimitProvider; import org.alfresco.repo.content.ContentStore; diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java index 9bd05bccad..a2017aa35f 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailRegistry.java @@ -57,17 +57,19 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi { /** Logger */ private static Log logger = LogFactory.getLog(ThumbnailRegistry.class); - + /** Content service */ private ContentService contentService; /** Transaction service */ - private TransactionService transactionService; + protected TransactionService transactionService; /** Rendition service */ - private RenditionService renditionService; + protected RenditionService renditionService; - private TenantAdminService tenantAdminService; + protected TenantAdminService tenantAdminService; + + private boolean redeployStaticDefsOnStartup; /** Map of thumbnail definition */ private Map thumbnailDefinitions = new HashMap(); @@ -124,7 +126,12 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi { this.tenantAdminService = tenantAdminService; } - + + public void setRedeployStaticDefsOnStartup(boolean redeployStaticDefsOnStartup) + { + this.redeployStaticDefsOnStartup = redeployStaticDefsOnStartup; + } + /** * This method is used to inject the thumbnail definitions. * @param thumbnailDefinitions @@ -348,12 +355,23 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi lifecycle.onApplicationEvent(event); } + protected boolean redeploy() + { + return AuthenticationUtil.runAs(new RunAsWork() + { + public Boolean doWork() throws Exception + { + return ((getThumbnailDefinitions().size() > 0) && (redeployStaticDefsOnStartup || renditionService.loadRenditionDefinitions().size() == 0)); + } + }, AuthenticationUtil.getSystemUserName()); + } + /** * This class hooks in to the spring application lifecycle and ensures that any * ThumbnailDefinitions injected by spring are converted to RenditionDefinitions * and saved. */ - private class RegistryLifecycle extends AbstractLifecycleBean + protected class RegistryLifecycle extends AbstractLifecycleBean { /* (non-Javadoc) * @see org.alfresco.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent) @@ -361,49 +379,52 @@ public class ThumbnailRegistry implements ApplicationContextAware, ApplicationLi @Override protected void onBootstrap(ApplicationEvent event) { - long start = System.currentTimeMillis(); - - // If the database is in read-only mode, then do not persist the thumbnail definitions. - if (transactionService.isReadOnly()) + if (redeploy()) { - if (logger.isDebugEnabled()) + long start = System.currentTimeMillis(); + + // If the database is in read-only mode, then do not persist the thumbnail definitions. + if (transactionService.isReadOnly()) { - logger.debug("TransactionService is in read-only mode. Therefore no thumbnail definitions have been initialised."); - } - return; - } - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - initThumbnailDefinitions(); - return null; - } - }, AuthenticationUtil.getSystemUserName()); - - if (tenantAdminService.isEnabled()) - { - List tenants = tenantAdminService.getAllTenants(); - for (Tenant tenant : tenants) - { - AuthenticationUtil.runAs(new RunAsWork() + if (logger.isDebugEnabled()) { - public Object doWork() throws Exception - { - initThumbnailDefinitions(); - return null; - } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); + logger.debug("TransactionService is in read-only mode. Therefore no thumbnail definitions have been initialised."); + } + return; + } + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + initThumbnailDefinitions(); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + if (tenantAdminService.isEnabled()) + { + List tenants = tenantAdminService.getAllTenants(); + for (Tenant tenant : tenants) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + initThumbnailDefinitions(); + return null; + } + }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); + } + } + + if (logger.isInfoEnabled()) + { + logger.info("Init'ed thumbnail defs in "+(System.currentTimeMillis()-start)+" ms"); } - } - - if (logger.isInfoEnabled()) - { - logger.info("Init'ed thumbnail defs in "+(System.currentTimeMillis()-start)+" ms"); } } - + /* (non-Javadoc) * @see org.alfresco.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent) */ diff --git a/source/java/org/alfresco/repo/version/VersionServiceImpl.java b/source/java/org/alfresco/repo/version/VersionServiceImpl.java index 76cc2738f4..43d5e8f71b 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImpl.java @@ -956,7 +956,7 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl implements Ve } // Do we need to create the initial version history entry? By convention this is always a major version. - if(getVersionHistory(nodeRef) == null) + if(getVersionHistoryNodeRef(nodeRef) == null) { createVersion(nodeRef, Collections.singletonMap(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR)); } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowNotificationUtils.java b/source/java/org/alfresco/repo/workflow/WorkflowNotificationUtils.java index e5c9d782c1..deb020e2d7 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowNotificationUtils.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowNotificationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import org.alfresco.repo.notification.EMailNotificationProvider; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.notification.NotificationContext; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -58,9 +59,10 @@ public abstract class WorkflowNotificationUtils public static final String ARG_WF_PRIORITY = "workflowPriority"; public static final String ARG_WF_POOLED = "workflowPooled"; public static final String ARG_WF_DOCUMENTS = "workflowDocuments"; + public static final String ARG_WF_TENANT = "workflowTenant"; /** Standard workflow assigned template */ - public static final NodeRef WF_ASSIGNED_TEMPLATE = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "wf-email-html-ftl"); + public static String WF_ASSIGNED_TEMPLATE = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, "wf-email-html-ftl").toString(); /** * @@ -99,7 +101,7 @@ public abstract class WorkflowNotificationUtils notificationContext.setSubject(subject); // Set the email template - notificationContext.setBodyTemplate(services.getFileFolderService().getLocalizedSibling(WF_ASSIGNED_TEMPLATE)); + notificationContext.setBodyTemplate(WF_ASSIGNED_TEMPLATE); // Build the template args MaptemplateArgs = new HashMap(7); @@ -135,6 +137,13 @@ public abstract class WorkflowNotificationUtils } } + // Add tenant, if in context of tenant + String tenant = TenantUtil.getCurrentDomain(); + if (tenant != null) + { + templateArgs.put(ARG_WF_TENANT, tenant); + } + // Set the template args notificationContext.setTemplateArgs(templateArgs); diff --git a/source/java/org/alfresco/service/cmr/notification/NotificationContext.java b/source/java/org/alfresco/service/cmr/notification/NotificationContext.java index 2385dd1ba5..a944b491b7 100644 --- a/source/java/org/alfresco/service/cmr/notification/NotificationContext.java +++ b/source/java/org/alfresco/service/cmr/notification/NotificationContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -46,7 +46,7 @@ public class NotificationContext private String body; /** Template node used to generate the body of the notification */ - private NodeRef bodyTemplate; + private String bodyTemplate; /** Template arguments (appear as map under 'arg' property in template model) */ private Map templateArgs; @@ -135,12 +135,12 @@ public class NotificationContext } /** - * The body template is a node reference to a template that can be executed with the given + * The body template is a node re or classpath ref to a template that can be executed with the given * template arguments to produce the body of the notification. * * @param bodyTemplate body template */ - public void setBodyTemplate(NodeRef bodyTemplate) + public void setBodyTemplate(String bodyTemplate) { this.bodyTemplate = bodyTemplate; } @@ -148,10 +148,10 @@ public class NotificationContext /** * @return {@link NodeRef} body template */ - public NodeRef getBodyTemplate() + public String getBodyTemplate() { return bodyTemplate; - } + } /** * The template arguments are used as context for the body template when it is executed. Any values placed in this map will diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java index 4715b3ec0d..613b26d4b2 100644 --- a/source/java/org/alfresco/service/cmr/security/PersonService.java +++ b/source/java/org/alfresco/service/cmr/security/PersonService.java @@ -102,7 +102,17 @@ public interface PersonService */ @Auditable(parameters = {"userName", "autoCreate"}) public NodeRef getPerson(final String userName, final boolean autoCreateHomeFolderAndMissingPersonIfAllowed); - + + /** + * Retrieve the person info for an existing {@code person NodeRef} + * + * @param person NodeRef + * @return PersonInfo (username, firstname, lastname) + * @throws NoSuchPersonException if the person doesn't exist + */ + @Auditable(parameters = {"personRef"}) + public PersonInfo getPerson(NodeRef personRef) throws NoSuchPersonException; + /** * Check if a person exists. *