From 2e79d2e6d26c812e5176427e26ff2ce8f0c50fc0 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 22 May 2007 04:47:14 +0000 Subject: [PATCH] Phase one of merge of EC multilingual work These files are their changes plus adjustments for formatting and immediate clashes I anticipate that this will break the build, but there are too many changes coming to risk it. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5740 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/auditConfig.xml | 6 + .../alfresco/bootstrap/multilingualRoot.xml | 4 +- config/alfresco/content-services-context.xml | 20 +- config/alfresco/core-services-context.xml | 153 ++++-- .../content-filter-languages.properties | 194 ++++++++ .../alfresco/messages/system-model.properties | 3 + config/alfresco/ml/content-filter-lang.xml | 455 ++++++++++++++++++ .../model-specific-services-context.xml | 20 +- config/alfresco/model/contentModel.xml | 18 +- config/alfresco/public-services-context.xml | 99 ++-- .../public-services-security-context.xml | 28 +- .../java/org/alfresco/model/ContentModel.java | 5 +- .../repo/content/RoutingContentService.java | 102 +++- .../repo/model/filefolder/FolderType.java | 119 +++++ .../repo/model/filefolder/FolderTypeTest.java | 114 +++++ .../model/ml/ContentFilterLanguagesMap.java | 268 +++++++++++ .../repo/model/ml/EmptyTranslationAspect.java | 160 ++++++ .../repo/model/ml/MLContainerType.java | 172 +++++++ .../ml/MultilingualContentServiceImpl.java | 255 ++++++++-- .../model/ml/MultilingualDocumentAspect.java | 305 ++++++++++++ .../repo/model/ml/MultilingualTestSuite.java | 63 +++ .../tools/AbstractMultilingualTestCases.java | 138 ++++++ .../tools/ContentFilterLanguagesMapTest.java | 146 ++++++ .../ml/tools/EmptyTranslationAspectTest.java | 220 +++++++++ .../model/ml/tools/MLContainerTypeTest.java | 148 ++++++ .../MultilingualContentServiceImplTest.java | 232 +++++++++ .../tools/MultilingualDocumentAspectTest.java | 220 +++++++++ .../repo/node/MLTranslationInterceptor.java | 181 +++++++ .../service/ServiceDescriptorRegistry.java | 11 +- .../alfresco/repo/version/VersionModel.java | 65 +-- .../org/alfresco/service/ServiceRegistry.java | 8 + .../cmr/ml/ContentFilterLanguagesService.java | 115 +++++ .../cmr/ml/MultilingualContentService.java | 35 ++ 33 files changed, 3885 insertions(+), 197 deletions(-) create mode 100644 config/alfresco/messages/content-filter-languages.properties create mode 100644 config/alfresco/ml/content-filter-lang.xml create mode 100644 source/java/org/alfresco/repo/model/filefolder/FolderType.java create mode 100644 source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java create mode 100644 source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java create mode 100644 source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java create mode 100644 source/java/org/alfresco/repo/model/ml/MLContainerType.java create mode 100644 source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java create mode 100644 source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java create mode 100644 source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java create mode 100644 source/java/org/alfresco/repo/node/MLTranslationInterceptor.java create mode 100644 source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java diff --git a/config/alfresco/auditConfig.xml b/config/alfresco/auditConfig.xml index bc2eb7e2cc..2186e28045 100644 --- a/config/alfresco/auditConfig.xml +++ b/config/alfresco/auditConfig.xml @@ -78,6 +78,12 @@ + + + + + + diff --git a/config/alfresco/bootstrap/multilingualRoot.xml b/config/alfresco/bootstrap/multilingualRoot.xml index 8269b67343..d1010338f8 100644 --- a/config/alfresco/bootstrap/multilingualRoot.xml +++ b/config/alfresco/bootstrap/multilingualRoot.xml @@ -7,8 +7,8 @@ GROUP_EVERYONE - Consumer - + Coordinator + diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index e07de6bc55..f438267488 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -30,7 +30,7 @@ - + @@ -99,6 +99,24 @@ + + + + + + classpath:alfresco/ml/content-filter-lang.xml + + + + + + + + + + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 0f3120d194..917f6f0656 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -114,10 +114,10 @@ value="${alfresco.jmx.dir}/alfresco-jmxrmi.access"/> - - - + + + @@ -155,12 +155,12 @@ - - - - - 20 - + + + + + 20 + @@ -171,7 +171,7 @@ false - + PROPAGATION_REQUIRED @@ -210,24 +210,25 @@ - - - alfresco.messages.system-messages - alfresco.messages.module-messages - alfresco.messages.dictionary-messages - alfresco.messages.version-service - alfresco.messages.permissions-service - alfresco.messages.content-service - alfresco.messages.coci-service - alfresco.messages.template-service - alfresco.messages.lock-service - alfresco.messages.patch-service - alfresco.messages.schema-update - alfresco.messages.webdav-messages - alfresco.messages.copy-service - alfresco.messages.avm-messages - - + + + alfresco.messages.system-messages + alfresco.messages.module-messages + alfresco.messages.dictionary-messages + alfresco.messages.version-service + alfresco.messages.permissions-service + alfresco.messages.content-service + alfresco.messages.coci-service + alfresco.messages.template-service + alfresco.messages.lock-service + alfresco.messages.patch-service + alfresco.messages.schema-update + alfresco.messages.webdav-messages + alfresco.messages.copy-service + alfresco.messages.avm-messages + alfresco.messages.content-filter-languages + + @@ -426,10 +427,10 @@ ${lucene.lock.poll.interval} - + EXACT_LANGUAGE_AND_ALL - + EXACT_LANGUAGE_AND_ALL @@ -547,23 +548,23 @@ - - + + - + - + - + - + - + @@ -585,7 +586,7 @@ - + @@ -636,23 +637,23 @@ - - - - - - - - - - + + + + + + + + + + - - - - + + + + @@ -825,6 +826,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/messages/content-filter-languages.properties b/config/alfresco/messages/content-filter-languages.properties new file mode 100644 index 0000000000..282c104741 --- /dev/null +++ b/config/alfresco/messages/content-filter-languages.properties @@ -0,0 +1,194 @@ +## Translations of ISO 639-1 languages codes + +content_filter_lang.aa=Afar +content_filter_lang.ab=Abkhazian +content_filter_lang.ae=Avestan +content_filter_lang.af=Afrikaans +content_filter_lang.ak=Akan +content_filter_lang.am=Amharic +content_filter_lang.an=Aragonese +content_filter_lang.ar=Arabic +content_filter_lang.as=Assamese +content_filter_lang.av=Avaric +content_filter_lang.ay=Aymara +content_filter_lang.az=Azerbaijani +content_filter_lang.ba=Bashkir +content_filter_lang.be=Belarusian +content_filter_lang.bg=Bulgarian +content_filter_lang.bh=Bihari +content_filter_lang.bi=Bislama +content_filter_lang.bm=Bambara +content_filter_lang.bn=Bengali +content_filter_lang.bo=Tibetan +content_filter_lang.br=Breton +content_filter_lang.bs=Bosnian +content_filter_lang.ca=Catalan; Valencian +content_filter_lang.ce=Chechen +content_filter_lang.ch=Chamorro +content_filter_lang.co=Corsican +content_filter_lang.cr=Cree +content_filter_lang.cs=Czech +content_filter_lang.cu=Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic +content_filter_lang.cv=Chuvash +content_filter_lang.cy=Welsh +content_filter_lang.da=Danish +content_filter_lang.de=German +content_filter_lang.dv=Divehi; Dhivehi; Maldivian +content_filter_lang.dz=Dzongkha +content_filter_lang.ee=Ewe +content_filter_lang.el=Greek +content_filter_lang.en=English +content_filter_lang.eo=Esperanto +content_filter_lang.es=Spanish +content_filter_lang.et=Estonian +content_filter_lang.eu=Basque +content_filter_lang.fa=Persian +content_filter_lang.ff=Fulah +content_filter_lang.fi=Finnish +content_filter_lang.fj=Fijian +content_filter_lang.fo=Faroese +content_filter_lang.fr=French +content_filter_lang.fy=Western Frisian +content_filter_lang.ga=Irish +content_filter_lang.gd=Gaelic; Scottish Gaelic +content_filter_lang.gl=Galician +content_filter_lang.gn=Guarani +content_filter_lang.gu=Gujarati +content_filter_lang.gv=Manx +content_filter_lang.ha=Hausa +content_filter_lang.he=Hebrew +content_filter_lang.hi=Hindi +content_filter_lang.ho=Hiri Motu +content_filter_lang.hr=Croatian +content_filter_lang.ht=Haitian; Haitian Creole +content_filter_lang.hu=Hungarian +content_filter_lang.hy=Armenian +content_filter_lang.hz=Herero +content_filter_lang.ia=Interlingua (International Auxiliary Language Association) +content_filter_lang.id=Indonesian +content_filter_lang.ie=Interlingue +content_filter_lang.ig=Igbo +content_filter_lang.ii=Sichuan Yi +content_filter_lang.ik=Inupiaq +content_filter_lang.io=Ido +content_filter_lang.is=Icelandic +content_filter_lang.it=Italian +content_filter_lang.iu=Inuktitut +content_filter_lang.ja=Japanese +content_filter_lang.jv=Javanese +content_filter_lang.ka=Georgian +content_filter_lang.kg=Kongo +content_filter_lang.ki=Kikuyu; Gikuyu +content_filter_lang.kj=Kuanyama; Kwanyama +content_filter_lang.kk=Kazakh +content_filter_lang.kl=Kalaallisut; Greenlandic +content_filter_lang.km=Khmer +content_filter_lang.kn=Kannada +content_filter_lang.ko=Korean +content_filter_lang.kr=Kanuri +content_filter_lang.ks=Kashmiri +content_filter_lang.ku=Kurdish +content_filter_lang.kv=Komi +content_filter_lang.kw=Cornish +content_filter_lang.ky=Kirghiz +content_filter_lang.la=Latin +content_filter_lang.lb=Luxembourgish; Letzeburgesch +content_filter_lang.lg=Ganda +content_filter_lang.li=Limburgan; Limburger; Limburgish +content_filter_lang.ln=Lingala +content_filter_lang.lo=Lao +content_filter_lang.lt=Lithuanian +content_filter_lang.lu=Luba-Katanga +content_filter_lang.lv=Latvian +content_filter_lang.mg=Malagasy +content_filter_lang.mh=Marshallese +content_filter_lang.mi=Maori +content_filter_lang.mk=Macedonian +content_filter_lang.ml=Malayalam +content_filter_lang.mn=Mongolian +content_filter_lang.mo=Moldavian +content_filter_lang.mr=Marathi +content_filter_lang.ms=Malay +content_filter_lang.mt=Maltese +content_filter_lang.my=Burmese +content_filter_lang.na=Nauru +content_filter_lang.nb=Norwegian Bokm\u00e5l +content_filter_lang.nd=Ndebele, North; North Ndebele +content_filter_lang.ne=Nepali +content_filter_lang.ng=Ndonga +content_filter_lang.nl=Dutch +content_filter_lang.nn=Norwegian Nynorsk +content_filter_lang.no=Norwegian +content_filter_lang.nr=Ndebele, South; South Ndebele +content_filter_lang.nv=Navajo; Navaho +content_filter_lang.ny=Chichewa; Chewa; Nyanja +content_filter_lang.oc=Occitan (post 1500); Proven\u00e7al +content_filter_lang.oj=Ojibwa +content_filter_lang.om=Oromo +content_filter_lang.or=Oriya +content_filter_lang.os=Ossetian; Ossetic +content_filter_lang.pa=Panjabi; Punjabi +content_filter_lang.pi=Pali +content_filter_lang.pl=Polish +content_filter_lang.ps=Pushto +content_filter_lang.pt=Portuguese +content_filter_lang.qu=Quechua +content_filter_lang.rm=Raeto-Romance +content_filter_lang.rn=Rundi +content_filter_lang.ro=Romanian +content_filter_lang.ru=Russian +content_filter_lang.rw=Kinyarwanda +content_filter_lang.sa=Sanskrit +content_filter_lang.sc=Sardinian +content_filter_lang.sd=Sindhi +content_filter_lang.se=Northern Sami +content_filter_lang.sg=Sango +content_filter_lang.si=Sinhala; Sinhalese +content_filter_lang.sk=Slovak +content_filter_lang.sl=Slovenian +content_filter_lang.sm=Samoan +content_filter_lang.sn=Shona +content_filter_lang.so=Somali +content_filter_lang.sq=Albanian +content_filter_lang.sr=Serbian +content_filter_lang.ss=Swati +content_filter_lang.st=Sotho, Southern +content_filter_lang.su=Sundanese +content_filter_lang.sv=Swedish +content_filter_lang.sw=Swahili +content_filter_lang.ta=Tamil +content_filter_lang.te=Telugu +content_filter_lang.tg=Tajik +content_filter_lang.th=Thai +content_filter_lang.ti=Tigrinya +content_filter_lang.tk=Turkmen +content_filter_lang.tl=Tagalog +content_filter_lang.tn=Tswana +content_filter_lang.to=Tonga (Tonga Islands) +content_filter_lang.tr=Turkish +content_filter_lang.tr=Turkish +content_filter_lang.ts=Tsonga +content_filter_lang.tt=Tatar +content_filter_lang.tw=Twi +content_filter_lang.ty=Tahitian +content_filter_lang.ug=Uighur; Uyghur +content_filter_lang.uk=Ukrainian +content_filter_lang.ur=Urdu +content_filter_lang.uz=Uzbek +content_filter_lang.ve=Venda +content_filter_lang.vi=Vietnamese +content_filter_lang.vo=Volap\u00fck +content_filter_lang.wa=Walloon +content_filter_lang.wo=Wolof +content_filter_lang.xh=Xhosa +content_filter_lang.yi=Yiddish +content_filter_lang.yo=Yoruba +content_filter_lang.za=Zhuang; Chuang +content_filter_lang.zh=Chinese +content_filter_lang.zu=Zulu + +## Duplicate messages to take in account the old +## ISO code. +content_filter_lang.iw=Hebrew +content_filter_lang.in=Indonesian +content_filter_lang.ji=Yiddish \ No newline at end of file diff --git a/config/alfresco/messages/system-model.properties b/config/alfresco/messages/system-model.properties index 44563c435f..61e6ae32da 100644 --- a/config/alfresco/messages/system-model.properties +++ b/config/alfresco/messages/system-model.properties @@ -18,6 +18,9 @@ sys_systemmodel.type.sys_reference.description=Reference sys_systemmodel.property.sys_reference.title=Reference sys_systemmodel.property.sys_reference.description=Reference +sys_systemmodel.property.sys_locale.title=Locale +sys_systemmodel.property.sys_locale.description=Locale + sys_systemmodel.aspect.aspect_root.title=Root sys_systemmodel.aspect.aspect_root.description=Root diff --git a/config/alfresco/ml/content-filter-lang.xml b/config/alfresco/ml/content-filter-lang.xml new file mode 100644 index 0000000000..e7ea15dc1e --- /dev/null +++ b/config/alfresco/ml/content-filter-lang.xml @@ -0,0 +1,455 @@ + + + + + ISO_639-1_EU-Order + + + + + + + + Afar + Abkhazian + Avestan + Afrikaans + Akan + Amharic + Aragonese + Arabic + Assamese + Avaric + Aymara + Azerbaijani + Bashkir + Belarusian + Bulgarian + Bihari + Bislama + Bambara + Bengali + Tibetan + Breton + Bosnian + Catalan; Valencian + Chechen + Chamorro + Corsican + Cree + Czech + + Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic + + Chuvash + Welsh + Danish + German + + Divehi; Dhivehi; Maldivian + + Dzongkha + Ewe + Greek + English + Esperanto + Spanish + Estonian + Basque + Persian + Fulah + Finnish + Fijian + Faroese + French + Western Frisian + Irish + + Gaelic; Scottish Gaelic + + Galician + Guarani + Gujarati + Manx + Hausa + Hebrew + Hindi + Hiri Motu + Croatian + + Haitian; Haitian Creole + + Hungarian + Armenian + Herero + + Interlingua (International Auxiliary Language Association) + + Indonesian + Interlingue + Igbo + Sichuan Yi + Inupiaq + Ido + Icelandic + Italian + Inuktitut + Japanese + Javanese + Georgian + Kongo + Kikuyu; Gikuyu + Kuanyama; Kwanyama + Kazakh + + Kalaallisut; Greenlandic + + Khmer + Kannada + Korean + Kanuri + Kashmiri + Kurdish + Komi + Cornish + Kirghiz + Latin + + Luxembourgish; Letzeburgesch + + Ganda + + Limburgan; Limburger; Limburgish + + Lingala + Lao + Lithuanian + Luba-Katanga + Latvian + Malagasy + Marshallese + Maori + Macedonian + Malayalam + Mongolian + Moldavian + Marathi + Malay + Maltese + Burmese + Nauru + Norvégien Bokmål + + Ndebele, North; North Ndebele + + Nepali + Ndonga + Dutch + Norwegian Nynorsk + Norwegian + + Ndebele, South; South Ndebele + + Navajo; Navaho + + Chichewa; Chewa; Nyanja + + + Occitan (post 1500); Provençal + + Ojibwa + Oromo + Oriya + Ossetian; Ossetic + Panjabi; Punjabi + Pali + Polish + Pushto + Portuguese + Quechua + Raeto-Romance + Rundi + Romanian + Russian + Kinyarwanda + Sanskrit + Sardinian + Sindhi + Northern Sami + Sango + Sinhala; Sinhalese + Slovak + Slovenian + Samoan + Shona + Somali + Albanian + Serbian + Swati + Sotho, Southern + Sundanese + Swedish + Swahili + Tamil + Telugu + Tajik + Thai + Tigrinya + Turkmen + Tagalog + Tswana + Tonga (Tonga Islands) + Turkish + Turkish + Tsonga + Tatar + Twi + Tahitian + Uighur; Uyghur + Ukrainian + Urdu + Uzbek + Venda + Vietnamese + Volapük + Walloon + Wolof + Xhosa + Yiddish + Yoruba + Zhuang; Chuang + Chinese + Zulu + + + + + + Bulgarian + Czech + Danish + German + Estonian + Greek + English + Spanish + French + Irish + Italian + Latvian + Lithuanian + Hungarian + Maltese + Dutch + Polish + Portuguese + Romanian + Slovak + Slovenian + Finnish + Swedish + Croatian + Turkish + Afar + Abkhazian + Afrikaans + Akan + Albanian + Amharic + Arabic + Aragonese + Armenian + Assamese + Avaric + Avestan + Aymara + Azerbaijani + Bashkir + Bambara + Basque + Belarusian + Bengali + Bihari + Bislama + Bosnian + Breton + Burmese + Catalan; Valencian + Chamorro + Chechen + Chinese + + Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic + + Chuvash + Cornish + Corsican + Cree + + Divehi; Dhivehi; Maldivian + + Dzongkha + Esperanto + Ewe + Faroese + Fijian + Western Frisian + Fulah + Georgian + + Gaelic; Scottish Gaelic + + Galician + Manx + Guarani + Gujarati + + Haitian; Haitian Creole + + Hausa + Hebrew + Herero + Hindi + Hiri Motu + Igbo + Icelandic + Ido + Sichuan Yi + Inuktitut + Interlingue + + Interlingua (International Auxiliary Language Association) + + Indonesian + Inupiaq + Javanese + Japanese + + Kalaallisut; Greenlandic + + Kannada + Kashmiri + Kanuri + Kazakh + Khmer + Kikuyu; Gikuyu + Kinyarwanda + Kirghiz + Komi + Kongo + Korean + Kuanyama; Kwanyama + Kurdish + Lao + Latin + + Limburgan; Limburger; Limburgish + + Lingala + + Luxembourgish; Letzeburgesch + + Luba-Katanga + Ganda + Macedonian + Marshallese + Malayalam + Maori + Marathi + Malay + Malagasy + Moldavian + Mongolian + Nauru + Navajo; Navaho + + Ndebele, South; South Ndebele + + + Ndebele, North; North Ndebele + + Ndonga + Nepali + Norwegian Nynorsk + Norvégien Bokmål + Norwegian + + Chichewa; Chewa; Nyanja + + + Occitan (post 1500); Provençal + + Ojibwa + Oriya + Oromo + Ossetian; Ossetic + Panjabi; Punjabi + Persian + Pali + Pushto + Quechua + Raeto-Romance + Rundi + Russian + Sango + Sanskrit + Serbian + Sinhala; Sinhalese + Northern Sami + Samoan + Shona + Sindhi + Somali + Sotho, Southern + Sardinian + Swati + Sundanese + Swahili + Tahitian + Tamil + Tatar + Telugu + Tajik + Tagalog + Thai + Tibetan + Tigrinya + Tonga (Tonga Islands) + Tswana + Tsonga + Turkmen + Turkish + Twi + Uighur; Uyghur + Ukrainian + Urdu + Uzbek + Venda + Vietnamese + Volapük + Welsh + Walloon + Wolof + Xhosa + Yiddish + Yoruba + Zhuang; Chuang + Zulu + + + + + diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 0625ba257b..d43567e6a3 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -40,11 +40,23 @@ - + - - - + + + + + + + + + + + + + + + diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index 3dde8d3303..eb7229ab35 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -19,7 +19,7 @@ \<\?\/\:\|\xA3\xAC\%\&\+\;]+.*)|(.*[\.]?.*[\.]+$)|(.*[ ]+$)]]> false - + @@ -143,12 +143,12 @@ Person sys:base - - + + d:text true - + @@ -260,6 +260,8 @@ cm:versionable + cm:author + sys:localized @@ -572,7 +574,7 @@ true true - + @@ -723,6 +725,12 @@ + + Empty Translation + + cm:mlDocument + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index b446ad07ad..e478ea6c91 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -67,6 +67,7 @@ + @@ -121,6 +122,10 @@ + + + + @@ -655,6 +660,15 @@ + + + + + + + + + @@ -723,14 +737,14 @@ - - - - - - - - + + + + + + + + @@ -820,7 +834,7 @@ - + @@ -838,7 +852,7 @@ - + @@ -885,21 +899,21 @@ - compare + compare - - + + - update - flatten - resetLayer + update + flatten + resetLayer @@ -921,8 +935,8 @@ - - + + @@ -930,25 +944,25 @@ - getAttributes - query - getKeys + getAttributes + query + getKeys exists getCount - - + + - setAttribute - removeAttribute - addAttribute + setAttribute + removeAttribute + addAttribute @@ -970,13 +984,13 @@ - - - + + + - + - + + - getLock - getUserLocks - getWebProjectLocks + getLock + getUserLocks + getWebProjectLocks @@ -1041,16 +1055,16 @@ - addWebProject - lockPath - removeLock - removeWebProject + addWebProject + lockPath + removeLock + removeWebProject - + org.alfresco.service.cmr.avm.locking.AVMLockingService @@ -1140,6 +1154,7 @@ + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 83cde8adc0..445e131556 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -61,7 +61,7 @@ - + @@ -519,6 +519,32 @@ + + + + + + + + + + + + + + org.alfresco.service.cmr.ml.MultilingualContentService.getTranslationContainer=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.MultilingualContentService.getTranslations=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.MultilingualContentService.getTranslationForLocale=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.MultilingualContentService.getMissingTranslations=ACL_ALLOW + org.alfresco.service.cmr.ml.MultilingualContentService.getPivotTranslation=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.MultilingualContentService.makeTranslation=ACL_NODE.0.sys:base.Write + org.alfresco.service.cmr.ml.MultilingualContentService.addTranslation=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.Write + org.alfresco.service.cmr.ml.MultilingualContentService.addEmptyTranslation=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.ml.MultilingualContentService.createEdition=ACL_NODE.0.sys:base.Write + org.alfresco.service.cmr.ml.MultilingualContentService.renameWithMLExtension=ACL_NODE.0.sys:base.Write + + + diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index 64801294af..82ad10748a 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -101,7 +101,7 @@ public interface ContentModel // copy aspect constants static final QName ASPECT_COPIEDFROM = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copiedfrom"); static final QName PROP_COPY_REFERENCE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "source"); - + // working copy aspect contants static final QName ASPECT_WORKING_COPY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingcopy"); static final QName PROP_WORKING_COPY_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "workingCopyOwner"); @@ -142,7 +142,7 @@ public interface ContentModel public final static QName PROP_LOCK_OWNER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockOwner"); public final static QName PROP_LOCK_TYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "lockType"); public final static QName PROP_EXPIRY_DATE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "expiryDate"); - + // version aspect static final QName ASPECT_VERSIONABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionable"); static final QName PROP_VERSION_LABEL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "versionLabel"); @@ -212,6 +212,7 @@ public interface ContentModel static final QName TYPE_MULTILINGUAL_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlContainer"); static final QName ASSOC_MULTILINGUAL_CHILD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlChild"); static final QName ASPECT_MULTILINGUAL_DOCUMENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlDocument"); + static final QName ASPECT_MULTILINGUAL_EMPTY_TRANSLATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlEmptyTranslation"); // // User Model Definitions diff --git a/source/java/org/alfresco/repo/content/RoutingContentService.java b/source/java/org/alfresco/repo/content/RoutingContentService.java index d20ca5adad..117c78c182 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentService.java +++ b/source/java/org/alfresco/repo/content/RoutingContentService.java @@ -27,10 +27,13 @@ package org.alfresco.repo.content; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; @@ -47,6 +50,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; @@ -59,6 +63,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; 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.Pair; @@ -143,9 +148,9 @@ public class RoutingContentService implements ContentService } public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } + { + this.policyComponent = policyComponent; + } public void setAvmService(AVMService service) { @@ -162,23 +167,23 @@ public class RoutingContentService implements ContentService */ public void init() { - // Bind on update properties behaviour - this.policyComponent.bindClassBehaviour( - QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), - this, - new JavaBehaviour(this, "onUpdateProperties")); - - // Register on content update policy - this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class); + // Bind on update properties behaviour + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + this, + new JavaBehaviour(this, "onUpdateProperties")); + + // Register on content update policy + this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class); this.onContentReadDelegate = this.policyComponent.registerClassPolicy(OnContentReadPolicy.class); } /** * Update properties policy behaviour * - * @param nodeRef the node reference - * @param before the before values of the properties - * @param after the after values of the properties + * @param nodeRef the node reference + * @param before the before values of the properties + * @param after the after values of the properties */ public void onUpdateProperties( NodeRef nodeRef, @@ -274,7 +279,7 @@ public class RoutingContentService implements ContentService } private ContentReader getReader(NodeRef nodeRef, QName propertyQName, boolean fireContentReadPolicy) - { + { ContentData contentData = null; Serializable propValue = nodeService.getProperty(nodeRef, propertyQName); if (propValue instanceof Collection) @@ -293,8 +298,9 @@ public class RoutingContentService implements ContentService if (contentData == null) { - // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY PropertyDefinition contentPropDef = dictionaryService.getProperty(propertyQName); + + // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY if (contentPropDef != null && (!(contentPropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT) || contentPropDef.getDataType().getName().equals(DataTypeDefinition.ANY)))) @@ -310,8 +316,66 @@ public class RoutingContentService implements ContentService // check that the URL is available if (contentData == null || contentData.getContentUrl() == null) { - // there is no URL - the interface specifies that this is not an error condition - return null; + // if the node is an empty translation, the reader must be the reader of its pivot translation + if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT) + && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + // NOTE : don't use the MultilingualContentService here because : + // A valid SecureContext can't be provided in the RequestContext + + List parentAssocRefs = nodeService.getParentAssocs( + nodeRef, + ContentModel.ASSOC_MULTILINGUAL_CHILD, + RegexQNamePattern.MATCH_ALL); + + if (parentAssocRefs.size() == 1) + { + // Get the ml container and its locale + NodeRef container = parentAssocRefs.get(0).getParentRef(); + Locale containerLocale = (Locale) nodeService.getProperty(container, ContentModel.PROP_LOCALE); + + // Get each translation + List assocRefs = nodeService.getChildAssocs( + container, + ContentModel.ASSOC_MULTILINGUAL_CHILD, + RegexQNamePattern.MATCH_ALL); + + + // found the pivot translation + NodeRef pivot = null; + + for(ChildAssociationRef assoc : assocRefs) + { + pivot = assoc.getChildRef(); + Locale pivotLocale = (Locale) nodeService.getProperty(pivot, ContentModel.PROP_LOCALE); + + if(containerLocale.equals(pivotLocale)) + { + break; // the pivot is set + } + else + { + pivot = null; + } + } + + // returns the reader of this pivot translation if it's different to + // the node in parameter + + if (pivot != null && !nodeRef.getId().equals(pivot.getId())) + { + return getReader(pivot, propertyQName, fireContentReadPolicy); + } + + } + } + else + { + // there is no URL - the interface specifies that this is not an error condition + return null; + } + + } String contentUrl = contentData.getContentUrl(); @@ -541,4 +605,4 @@ public class RoutingContentService implements ContentService } } } -} +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/filefolder/FolderType.java b/source/java/org/alfresco/repo/model/filefolder/FolderType.java new file mode 100644 index 0000000000..919fd9a866 --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/FolderType.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder; + +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + + +/** + * Class containing behaviour for the folder type. + * + * {@link ContentModel#TYPE_FOLDER folder type} + * + * @author yanipig + */ +public class FolderType implements + NodeServicePolicies.BeforeDeleteNodePolicy +{ + + // Dependencies + private PolicyComponent policyComponent; + private NodeService nodeService; + + + /** + * Initialise the Folder type + * + * Ensures that the {@link ContentModel#TYPE_FOLDER} folder type + */ + public void init() + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.TYPE_FOLDER, + new JavaBehaviour(this, "beforeDeleteNode")); + + } + + /** + * @param policyComponent the policy component to register behaviour with + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + + /** + * Since the multilingual document don't keep their mlDocument aspect, the mlEmptyTranslation + * can't be archived and the deletion of a container doesn't call the right policies, the deletion of a + * folder must explicitly remove this aspect. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeDeleteNode(NodeRef nodeRef) + { + List childAssociations = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + + for(ChildAssociationRef childAssoc : childAssociations) + { + NodeRef child = childAssoc.getChildRef(); + + if(nodeService.exists(child)) + { + // Remove the mlDocument aspect of each multilingual node contained in the folder. + // The policies of this aspect perform the right logic. + if(nodeService.hasAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + nodeService.removeAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + // Recurse the process if needed + else if(nodeService.getType(child).equals(ContentModel.TYPE_FOLDER)) + { + beforeDeleteNode(child); + } + } + } + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java b/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java new file mode 100644 index 0000000000..aa93590fa4 --- /dev/null +++ b/source/java/org/alfresco/repo/model/filefolder/FolderTypeTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.filefolder; + +import java.util.Locale; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.ml.tools.AbstractMultilingualTestCases; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Multilingual container type test cases + * + * @see org.alfresco.service.cmr.ml.MLContainerType + * + * @author yanipig + */ +public class FolderTypeTest extends AbstractMultilingualTestCases { + + + @SuppressWarnings("unused") + public void testRemoveSpace() throws Exception + { + NodeRef pivot = createContent(); + NodeRef trans2 = createContent(); + NodeRef trans3 = createContent(); + NodeRef empty; + + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + multilingualContentService.addTranslation(trans2, pivot, Locale.GERMAN); + multilingualContentService.addTranslation(trans3, pivot, Locale.ITALIAN);; + empty = multilingualContentService.addEmptyTranslation(pivot, "Empty_" + System.currentTimeMillis(), Locale.ENGLISH); + + + NodeRef space = fileFolderService.create( + nodeService.getPrimaryParent(pivot).getParentRef(), + "folder_" + System.currentTimeMillis(), + ContentModel.TYPE_FOLDER).getNodeRef(); + + nodeService.moveNode(pivot, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(pivot).getQName()); + nodeService.moveNode(trans2, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(trans2).getQName()); + nodeService.moveNode(trans3, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(trans3).getQName()); + nodeService.moveNode(empty, space, ContentModel.ASSOC_CONTAINS, this.nodeService.getPrimaryParent(empty).getQName()); + + // Ensure that the nodes are correctly moved + assertEquals("Move nodes failed", 4, nodeService.getChildAssocs(space).size()); + + // 1. Delete space + + nodeService.deleteNode(space); + + // Ensute that the space is deleted + assertFalse("The deletion of the space failed", nodeService.exists(space)); + + // Ensure that the nodes are archived + assertTrue("The space " + space + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); + assertTrue("The node " + pivot + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); + assertTrue("The node " + trans2 + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); + assertTrue("The node " + trans3 + " must be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); + + // Ensure that the mlContainer is deleted and not archived + assertFalse("The mlContainer " + mlContainer + " must be deleted", nodeService.exists(mlContainer)); + assertFalse("The mlContainer " + mlContainer + " can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(mlContainer))); + + // 2. Restore space + nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(space)); + + // Ensure that the nodes are restaured + assertFalse("The space " + space + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(space))); + assertFalse("The node " + pivot + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); + assertFalse("The node " + trans2 + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); + assertFalse("The node " + trans3 + "must be restored", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); + + // Ensure that the mlContainer is not restored + assertFalse("The mlContainer " + mlContainer + " must be deleted and can't be restored", nodeService.exists(mlContainer)); + + // 3. Delete space and remove it from the arhives + + nodeService.deleteNode(space); + nodeService.deleteNode(nodeArchiveService.getArchivedNode(space)); + + assertFalse("The space " + space + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(space))); + assertFalse("The node " + pivot + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(pivot))); + assertFalse("The node " + trans2 + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans2))); + assertFalse("The node " + trans3 + "can't be archived", nodeService.exists(nodeArchiveService.getArchivedNode(trans3))); + + assertFalse("The space " + space + "must be deleted", nodeService.exists(space)); + assertFalse("The node " + pivot + "must be deleted", nodeService.exists(pivot)); + assertFalse("The node " + trans2 + "must be deleted", nodeService.exists(trans2)); + assertFalse("The node " + trans3 + "must be deleted", nodeService.exists(trans3)); + } +} diff --git a/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java b/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java new file mode 100644 index 0000000000..c80b30470b --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/ContentFilterLanguagesMap.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.config.Config; +import org.alfresco.config.ConfigElement; +import org.alfresco.config.ConfigLookupContext; +import org.alfresco.config.ConfigService; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Provides a an implementation of the Content Filter Languages Service + * + * {@link org.alfresco.service.cmr.ml.ContentFilterLanguagesService Content Filter Languages Service} + * + * @author yanipig + */ +public class ContentFilterLanguagesMap implements ContentFilterLanguagesService +{ + private static final String CONFIG_AREA = "content-filter-lang"; + + private static final String CONFIG_CONDITION = "Languages Filter Map"; + + private static final String USED_STANDARD_CONFIG_CONDITION = "Standard In Use"; + private static final String USED_STANDARD_ELEMENT = "standard"; + + private static final String DEFAULT_LANGUAGE_LIST_STANDARD = "ISO 639-1"; + + private static final String ATTR_CODE = "code"; + private static final String ATTR_ORDER = "order"; + private static final String ATTR_DEFAULT = "default"; + + private static final Log logger = LogFactory.getLog(ContentFilterLanguagesMap.class); + + private ConfigService configService; + + private List orderedLangCodes; + private Map languagesByCode; + + private String defaultLanguage = null; + + /** + * @param configService the config service to use to read languages + */ + public ContentFilterLanguagesMap(ConfigService configService) + { + this.configService = configService; + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#getFilterLanguages() + */ + public List getFilterLanguages() + { + return orderedLangCodes; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#getMissingLanguages(java.util.List) + */ + public List getMissingLanguages(List availableLanguages) + { + + if(availableLanguages == null || availableLanguages.size() == 0) + { + return orderedLangCodes; + } + + List returnList = new ArrayList(orderedLangCodes.size() - availableLanguages.size()); + + int index = 0; + + for(String lang : orderedLangCodes) + { + if(!availableLanguages.contains(lang)) + { + returnList.add(index, lang); + index ++; + } + } + return returnList; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#getLabelByCode(java.lang.String) + */ + public String getLabelByCode(String code) + { + // get the translated language label + String label; + + label = I18NUtil.getMessage(MESSAGE_PREFIX + code); + + // if not found, get the default name (found in content-filter-lang.xml) + if(label == null || label.length() == 0) + { + label = languagesByCode.get(code); + } + + // if not found, return the language code + if(label == null || label.length() == 0) + { + label = code + " (label not found)"; + } + + return label; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#getOrderByCode(java.lang.String) + */ + public int getOrderByCode(String code) + { + if (orderedLangCodes.contains(code)) + { + return orderedLangCodes.indexOf(code); + } + else + { + throw new AlfrescoRuntimeException("Language code not found : " + code); + } + } + + public String getDefaultLanguage() + { + return this.defaultLanguage ; + } + + /** + * Initialises the map using the configuration service provided + */ + public void init() + { + ConfigLookupContext clContext = new ConfigLookupContext(CONFIG_AREA); + + // get which standart is defined by the user (ISO 639-1 by default) + String standard = DEFAULT_LANGUAGE_LIST_STANDARD; + + Config configStandard = configService.getConfig(USED_STANDARD_CONFIG_CONDITION, clContext); + + if(configStandard != null + && configStandard.getConfigElement(USED_STANDARD_ELEMENT) != null) + { + standard = configStandard.getConfigElement(USED_STANDARD_ELEMENT).getValue(); + } + else + { + logger.warn("No standard configured, use by default : " + DEFAULT_LANGUAGE_LIST_STANDARD); + } + + + Config configConditions = configService.getConfig(CONFIG_CONDITION, clContext); + Map configElements = configConditions.getConfigElements(); + + ConfigElement configLanguages = null; + + // get the list of languages of the matched standard + if(configElements.containsKey(standard)) + { + configLanguages = configElements.get(standard); + } + + // if the santard is not matched, get the first list + else + { + configLanguages = configElements.values().iterator().next(); + logger.warn("Ignoring prefered standard doesn't found, choose : " + configLanguages.getName()); + } + + List languages = configLanguages.getChildren(); + + // get the size of the lists + int listSize = languages.size(); + this.orderedLangCodes = new ArrayList(listSize); + this.languagesByCode = new HashMap(listSize); + + // construct the languages map and list + for (ConfigElement langElem : languages) + { + String code = convertToOldISOCode(langElem.getAttribute(ATTR_CODE)); + String order = langElem.getAttribute(ATTR_ORDER); + String value = langElem.getValue(); + String def = langElem.getAttribute(ATTR_DEFAULT); + + orderedLangCodes.add(Integer.parseInt(order) - 1, code); + + languagesByCode.put(code, value); + + if(def != null && Boolean.parseBoolean(def)) + { + if(defaultLanguage != null) + { + logger.warn("Ignoring default attribute is not unique le last matched will be used"); + } + + this.defaultLanguage = code; + } + } + + // make the collections read-only + this.orderedLangCodes = Collections.unmodifiableList(this.orderedLangCodes); + this.languagesByCode = Collections.unmodifiableMap(this.languagesByCode); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#convertToOldISOCode(java.lang.String) + */ + public String convertToOldISOCode(String code) + { + if(code.equalsIgnoreCase("he")) + code = "iw"; + else if(code.equalsIgnoreCase("id")) + code = "in"; + else if(code.equalsIgnoreCase("yi")) + code = "ji"; + + return code; + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService#convertToNewISOCode(java.lang.String) + */ + public String convertToNewISOCode(String code) { + + if(code.equalsIgnoreCase("iw")) + code = "he"; + else if(code.equalsIgnoreCase("in")) + code = "id"; + else if(code.equalsIgnoreCase("ji")) + code = "yi"; + + return code; + } +} diff --git a/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java new file mode 100644 index 0000000000..f80ae6c59a --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/EmptyTranslationAspect.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.model.ml; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.ContentServicePolicies; +import org.alfresco.repo.copy.CopyServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.PolicyScope; +import org.alfresco.service.cmr.repository.ContentData; +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.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Class containing behaviour for the multilingual empty translation aspect. + * An empty translation is a document's properties translation. This kind of node + * doesn't have a content. + * + * {@link ContentModel#ASPECT_MULTILINGUAL_EMPTY_TRANSLATION ml empty document aspect} + * + * @author yanipig + */ +public class EmptyTranslationAspect implements + CopyServicePolicies.OnCopyNodePolicy, + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnRemoveAspectPolicy, + ContentServicePolicies.OnContentUpdatePolicy +{ + + // Dependencies + private PolicyComponent policyComponent; + + private NodeService nodeService; + + + /** + * Initialise the Multilingual Empty Translation Aspect + *

+ * Ensures that the {@link ContentModel#ASPECT_MULTILINGUAL_EMPTY_TRANSLATION ml empty document aspect} + */ + public void init() + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"), + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, + new JavaBehaviour(this, "onCopyNode")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"), + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, + new JavaBehaviour(this, "onContentUpdate")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, + new JavaBehaviour(this, "beforeDeleteNode")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, + new JavaBehaviour(this, "onRemoveAspect")); + } + + /** + * @param policyComponent the policy component to register behaviour with + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Copy a cm:mlEmptyTranslation is not permit. + * + * @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope) + */ + public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails) + { + throw new IllegalStateException("It's impossible to copy an empty translation"); + } + + /** + * If a content is added to a cm:mlEmptyTranslation, remove this aspect. + * + * @see org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy#onContentUpdate(org.alfresco.service.cmr.repository.NodeRef, boolean) + */ + public void onContentUpdate(NodeRef nodeRef, boolean newContent) + { + if(newContent) + { + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + } + } + + /** + * If a cm:mlEmptyTranslation is deleted, it can't be archived. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeDeleteNode(NodeRef nodeRef) + { + // add TEMPORARY ASPECT to force the deleteNode + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); + } + + /** + * If the aspect cm:mlEmptyTranslation is removed and the content url property is null, the node can't be store much time. + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) * + */ + public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) + { + // if the node is updated, the URL will not be null and the rome aspect don't delete + // the translation. It will be a simple mlDocument. + if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION) + && ((ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT)).getContentUrl() == null) + { + // add TEMPORARY ASPECT to force the deleteNode + nodeService.addAspect(nodeRef, ContentModel.ASPECT_TEMPORARY, null); + nodeService.deleteNode(nodeRef); + } + } +} diff --git a/source/java/org/alfresco/repo/model/ml/MLContainerType.java b/source/java/org/alfresco/repo/model/ml/MLContainerType.java new file mode 100644 index 0000000000..6be5a0a55d --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/MLContainerType.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.model.ml; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Class containing behaviour for the multilingual multilingual container type. + * A multilingual container type is fonctionally named 'Logical Document'. + * It links each translation of a semantical message together + * + * {@link ContentModel#TYPE_MULTILINGUAL_CONTAINER multilingual container type} + * + * @author yanipig + */ +public class MLContainerType implements + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.OnUpdatePropertiesPolicy +{ + /** + * set a property with this QName when the deletion process is running on the mlContainer. In this case + * the policies of the remove aspect of mlDocument don't delete one more time the mlContainer. + */ + /*package*/ static QName PROP_NAME_DELETION_RUNNING = QName.createQName("__MLContanier_", "Deletion_Running"); + + // Dependencies + private PolicyComponent policyComponent; + + private NodeService nodeService; + + private MultilingualContentService multilingualContentService; + + /** + * Initialise the Multilingual Container Type + *

+ * Ensures that the {@link ContentModel#ASPECT_MULTILINGUAL_EMPTY_TRANSLATION ml empty document aspect} + */ + public void init() + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ContentModel.TYPE_MULTILINGUAL_CONTAINER, + new JavaBehaviour(this, "onUpdateProperties")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.TYPE_MULTILINGUAL_CONTAINER, + new JavaBehaviour(this, "beforeDeleteNode")); + } + + /** + * @param policyComponent the policy component to register behaviour with + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param multilingualContentService the Multilingual Content Service to set + */ + public void setMultilingualContentService( + MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * If a cm:mlContainer is deleted, it can't be archived. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeDeleteNode(NodeRef nodeRef) + { + Map translations = multilingualContentService.getTranslations(nodeRef); + + // add the DELETION_RUNNING property + nodeService.setProperty(nodeRef, PROP_NAME_DELETION_RUNNING, new Boolean(true)); + + for(Map.Entry entry : translations.entrySet()) + { + nodeService.removeAspect(entry.getValue(), ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + + // remove the DELETION_RUNNING property + Map prop = nodeService.getProperties(nodeRef); + prop.remove(PROP_NAME_DELETION_RUNNING); + nodeService.setProperties(nodeRef, prop); + + } + + /** + * The property locale of a cm:mlContainer represents the locale of the pivot translation. + * + * Since the pivot must be an existing translation and the pivot can t be empty, some tests must be performed when + * the locale of the mlContainer is updated. + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map) + */ + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + Locale localeAfter = (Locale) after.get(ContentModel.PROP_LOCALE); + Locale localeBefore = (Locale) before.get(ContentModel.PROP_LOCALE); + + // The locale can be set as null if the container have no children. + // Normaly, it's ONLY the case at the creation of the container. + if(localeAfter == null && nodeService.getChildAssocs(nodeRef).size() != 0) + { + throw new IllegalArgumentException("A Locale property must be defined for a Multilingual Container and can't be null"); + } + + if(localeAfter != null && !localeAfter.equals(localeBefore)) + { + Map translations = multilingualContentService.getTranslations(nodeRef); + + //get the new pivot translation + NodeRef pivot = translations.get(localeAfter); + + if(pivot == null) + { + throw new IllegalArgumentException("The pivot translation must be an existing translation"); + } + else if(nodeService.hasAspect(pivot, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + throw new IllegalArgumentException("The pivot translation can't be an empty translation"); + } + } + } +} diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index 240c113c1e..2660cda30a 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -15,15 +15,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.model.ml; +import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -33,8 +35,12 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -52,19 +58,22 @@ import org.apache.commons.logging.LogFactory; /** * Multilingual support implementation - * + * * @author Derek Hulley * @author Philippe Dubois + * @author yanipig */ public class MultilingualContentServiceImpl implements MultilingualContentService { private static Log logger = LogFactory.getLog(MultilingualContentServiceImpl.class); - + private NodeService nodeService; private SearchService searchService; private VersionService versionService; private SearchParameters searchParametersMLRoot; - + private ContentFilterLanguagesService contentFilterLanguagesService; + private FileFolderService fileFolderService; + public MultilingualContentServiceImpl() { searchParametersMLRoot = new SearchParameters(); @@ -73,27 +82,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic searchParametersMLRoot.addStore(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore")); searchParametersMLRoot.setQuery("/cm:multilingualRoot"); } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - - public void setVersionService(VersionService versionService) - { - this.versionService = versionService; - } - - public void renameWithMLExtension(NodeRef translationNodeRef) - { - throw new UnsupportedOperationException(); - } - /** * @return Returns a reference to the node that will hold all the cm:mlContainer nodes. */ @@ -119,7 +108,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic rs.close(); } } - + private static final QName QNAME_ML_CONTAINER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlContainer"); private static final QName QNAME_ML_TRANSLATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlTranslation"); /** @@ -137,11 +126,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // done return assocRef.getChildRef(); } - + /** * Retrieve or create a cm:mlDocument container for the given node, which must have the * cm:mlDocument already applied. - * + * * @param mlDocumentNodeRef an existing cm:mlDocument * @param allowCreate true if a cm:mlContainer must be created if on doesn't exist, * otherwise false if a parent cm:mlContainer is expected to exist. @@ -225,12 +214,15 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // The aspect is present, so just ensure that the locale is correct nodeService.setProperty(contentNodeRef, ContentModel.PROP_LOCALE, locale); } - + // Do we make use of an existing container? if (mlContainerNodeRef == null) { // Make one mlContainerNodeRef = getOrCreateMLContainer(contentNodeRef, true); + + // set the pivot language + nodeService.setProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE, locale); } else { @@ -240,7 +232,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic { throw new AlfrescoRuntimeException("Duplicate locale in document pool: " + locale); } - + // Use the existing container nodeService.addChild( mlContainerNodeRef, @@ -248,6 +240,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic ContentModel.ASSOC_MULTILINGUAL_CHILD, QNAME_ML_TRANSLATION); } + // done return mlContainerNodeRef; } @@ -266,7 +259,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic } return mlContainerNodeRef; } - + /** @inheritDoc */ public NodeRef addTranslation(NodeRef newTranslationNodeRef, NodeRef translationOfNodeRef, Locale locale) { @@ -307,7 +300,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic /** @inheritDoc */ public void createEdition( NodeRef translationNodeRef) { - NodeRef mlContainerNodeRef = getOrCreateMLContainer(translationNodeRef, false); + NodeRef mlContainerNodeRef = getOrCreateMLContainer(translationNodeRef, false); // Ensure that the translation given is one of the children getOrCreateMLContainer(translationNodeRef, false); // Get all the container's children @@ -315,8 +308,25 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic mlContainerNodeRef, ContentModel.ASSOC_MULTILINGUAL_CHILD, RegexQNamePattern.MATCH_ALL); - // Version the container and all its children - versionService.createVersion(mlContainerNodeRef, null, true); + + + // Get and store the translation verions associated to the mlContainer + List versions = new ArrayList(childAssocRefs.size()); + + for (ChildAssociationRef childAssoc : childAssocRefs) + { + versions.add(versionService.getCurrentVersion(childAssoc.getChildRef())); + } + + Map editionProperties = new HashMap(); + editionProperties.put( + VersionModel.PROP_QNAME_TRANSLATION_VERIONS.toString(), + (Serializable) versions + ); + + // Version the container and all its children + versionService.createVersion(mlContainerNodeRef, editionProperties, true); + // Remove all the child documents apart from the given node boolean found = false; for (ChildAssociationRef childAssoc : childAssocRefs) @@ -416,4 +426,173 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic } return nearestNodeRef; } -} + + /** @inheritDoc */ + public List getMissingTranslations(NodeRef localizedNodeRef, boolean addThisNodeLocale) + { + List foundLocales = new ArrayList(getTranslations(localizedNodeRef).keySet()); + List foundLanguages = new ArrayList(); + + + // transform locales into languages codes + for(Locale locale : foundLocales) + { + foundLanguages.add(locale.getLanguage()); + } + + // add the locale of the given node if required + if(addThisNodeLocale) + { + Locale localeNode = (Locale) nodeService.getProperty(localizedNodeRef, ContentModel.PROP_LOCALE); + + if(localeNode != null) + { + foundLanguages.remove(localeNode.toString()); + } + else + { + logger.warn("No locale found for the node " + localizedNodeRef); + } + } + + List missingLanguages = null; + + if(foundLanguages.size() == 0) + { + // The given node is the only one available translation and it must + // be return. + // MissingLanguages become the entire list pf languages. + missingLanguages = contentFilterLanguagesService.getFilterLanguages(); + } + else + { + // get the missing languages form the list of content filter languages + missingLanguages = contentFilterLanguagesService.getMissingLanguages(foundLanguages); + } + // construct a list of locales + List missingLocales = new ArrayList(missingLanguages.size() + 1); + + for(String lang : missingLanguages) + { + missingLocales.add(I18NUtil.parseLocale(lang)); + } + + return missingLocales; + } + + /** @inheritDoc */ + public NodeRef getPivotTranslation(NodeRef nodeRef) + { + if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + NodeRef container = getTranslationContainer(nodeRef); + Locale containerLocale = (Locale) nodeService.getProperty(container, ContentModel.PROP_LOCALE); + + return getTranslationForLocale(nodeRef, containerLocale); + } + else if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(nodeRef))) + { + Locale containerLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); + + return getTranslationForLocale(getTranslations(nodeRef).get(containerLocale), containerLocale); + } + else + { + logger.warn("The node is not multilingual " + nodeRef); + + return null; + } + } + + /** + * @inheritDoc */ + public NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale) + { + // any node used as reference + NodeRef anyTranslation; + // the empty document to create + NodeRef newTranslationNodeRef = null; + + QName typeQName = nodeService.getType(translationOfNodeRef); + if (typeQName.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) + { + // Set the ml container ans get the pivot + anyTranslation = getPivotTranslation(translationOfNodeRef); + } + else if(nodeService.hasAspect(translationOfNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + anyTranslation = translationOfNodeRef; + } + else + { + throw new IllegalArgumentException( + "Node must have aspect " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " applied or be a " + ContentModel.TYPE_MULTILINGUAL_CONTAINER); + } + + // Create the document in the space of the node of reference + NodeRef parentNodeRef = nodeService.getPrimaryParent(anyTranslation).getParentRef(); + + newTranslationNodeRef = fileFolderService.create( + parentNodeRef, + name, + ContentModel.TYPE_CONTENT).getNodeRef(); + + // add the translation to the container + addTranslation(newTranslationNodeRef, translationOfNodeRef, locale); + + // set it empty + nodeService.addAspect(newTranslationNodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION, null); + + // get the extension and set the ContentData property with an null URL. + String extension = ""; + int dotIdx; + if((dotIdx = name.lastIndexOf(".")) > -1 ) + { + extension = name.substring(dotIdx); + } + + nodeService.setProperty(newTranslationNodeRef, ContentModel.PROP_CONTENT, + new ContentData(null, extension, 0, "UTF-8", locale)); + + + if (logger.isDebugEnabled()) + { + logger.debug("Added an empty translation: \n" + + " Translation of: " + translationOfNodeRef + " of type " + typeQName + "\n" + + " New translation: " + newTranslationNodeRef + "\n" + + " Locale: " + locale); + } + + return newTranslationNodeRef; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + public void setContentFilterLanguagesService(ContentFilterLanguagesService contentFilterLanguagesService) + { + this.contentFilterLanguagesService = contentFilterLanguagesService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public void renameWithMLExtension(NodeRef translationNodeRef) + { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java new file mode 100644 index 0000000000..846c8ed10b --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/MultilingualDocumentAspect.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.copy.CopyServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.PolicyScope; +import org.alfresco.service.cmr.ml.MultilingualContentService; +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.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; + + +/** + * Class containing behaviour for the multilingual document aspect. + * + * {@link ContentModel#ASPECT_MULTILINGUAL_DOCUMENT ml document aspect} + * + * @author yanipig + */ +public class MultilingualDocumentAspect implements + CopyServicePolicies.OnCopyNodePolicy, + CopyServicePolicies.OnCopyCompletePolicy, + NodeServicePolicies.BeforeDeleteNodePolicy, + NodeServicePolicies.BeforeRemoveAspectPolicy, + NodeServicePolicies.OnRemoveAspectPolicy, + NodeServicePolicies.OnUpdatePropertiesPolicy +{ + + // Dependencies + private PolicyComponent policyComponent; + + private MultilingualContentService multilingualContentService; + + private NodeService nodeService; + + + /** + * Initialise the Multilingual Aspect + * + * Ensures that the {@link ContentModel#ASPECT_MULTILINGUAL_DOCUMENT ml document aspect} + */ + public void init() + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onCopyNode")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "beforeDeleteNode")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRemoveAspect"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "beforeRemoveAspect")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onUpdateProperties")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onCopyComplete")); + + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"), + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT, + new JavaBehaviour(this, "onRemoveAspect")); + + } + + /** + * @param policyComponent the policy component to register behaviour with + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param multilingualContentService the Multilingual Content Service to set + */ + public void setMultilingualContentService( + MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + /** + * @param nodeService the Node Service to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * The copy of a cm:mlDocument can't keep the Multilingual aspect. + * + * @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope) + */ + public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails) + { + copyDetails.removeAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + + /** + * The copy of mlDocument don't keep the 'locale' property. + * + * @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyCompletePolicy#onCopyComplete(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, boolean, java.util.Map) + */ + public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef destinationRef, boolean copyToNewNode, Map copyMap) + { + Map copiedProp = nodeService.getProperties(destinationRef); + copiedProp.remove(ContentModel.PROP_LOCALE); + nodeService.setProperties(destinationRef, copiedProp); + } + + /** + * A cm:mlDocument is pivot translation it is a multilingual document (non empty) if it's language matches the language + * of its cm:mlDocument. And a pivot translation can't be removed if it's not the last translation of the cm:mlContainer. + * + * If a translation is deleted, it's multilingual aspect is lost. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy#beforeDeleteNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeDeleteNode(NodeRef nodeRef) + { + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + } + + /** + * When a pivot translation is deleted, it's cm:mlContainer is deleted too. + * + * Note: When the pivot translation and the mlContainer are deleted, the mlDocument apsect is removed from + * the other translations. It will be better to don't allow the deletion of the pivot if other translation is + * available at the client side level. + * + * @see org.alfresco.repo.node.NodeServicePolicies.BeforeRemoveAspectPolicy#beforeRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void beforeRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef); + + // nothing to do if the mlContainer is in a deletion process + Boolean inDeleteProcess = (Boolean) nodeService.getProperty(mlContainer, MLContainerType.PROP_NAME_DELETION_RUNNING); + if(inDeleteProcess != null && inDeleteProcess == true) + { + return; + } + + Locale mlContainerLocale = (Locale) nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE); + Locale nodeRefLocale = (Locale) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCALE); + + nodeService.removeChild(mlContainer, nodeRef); + + // if last translation or if nodeRef is pivot translation + if(multilingualContentService.getTranslations(mlContainer).size() == 0 + || mlContainerLocale.equals(nodeRefLocale)) + { + // delete the mlContainer + nodeService.deleteNode(mlContainer); + } + } + } + + /** + * After removing the cm:mlDocument aspect : + * - the node is removed is it's a cm:mlEmptyTranslation + * - if not, only the locale property is removed + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if(aspectTypeQName.equals(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + + if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + { + // note: if the node has the temporary aspect and the mlEmptyTranslation in a same + // time, it means that the node is being deleted by another process... + if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY)) + { + nodeService.deleteNode(nodeRef); + } + } + else + { + Map props = nodeService.getProperties(nodeRef); + props.remove(ContentModel.PROP_LOCALE); + nodeService.setProperties(nodeRef, props); + + } + } + } + + /** + * Ensure that the locale is unique inside the mlContainer. + * + * If the locale of a pivot translation is modified, the pivot locale reference of the mlContainer + * must be modified too. + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map) + */ + public void onUpdateProperties(NodeRef nodeRef, Map before, Map after) + { + Locale localeBefore = (Locale) before.get(ContentModel.PROP_LOCALE); + Locale localeAfter; + + // the after local property type can be either Locale or String + Serializable objLocaleAfter = after.get(ContentModel.PROP_LOCALE); + if (objLocaleAfter instanceof Locale ) + { + localeAfter = (Locale) objLocaleAfter; + + } + else + { + localeAfter = I18NUtil.parseLocale(objLocaleAfter.toString()); + } + + // if the local has been modified + if(!localeBefore.equals(localeAfter)) + { + NodeRef mlContainer = multilingualContentService.getTranslationContainer(nodeRef); + + // Since the map returned by the getTranslations doesn't duplicate keys, the size of this map will be + // different of the size of the number of children of the mlContainer if a duplicate locale is found. + int transSize = multilingualContentService.getTranslations(mlContainer).size(); + int childSize = nodeService.getChildAssocs(mlContainer, ContentModel.ASSOC_MULTILINGUAL_CHILD, RegexQNamePattern.MATCH_ALL).size(); + + // if duplicate locale found + if(transSize != childSize) + { + // throw an exception and the current transaction will be rolled back. The properties will not be + // longer in an illegal state. + throw new IllegalArgumentException("The locale " + localeAfter + + " can't be changed for the node " + nodeRef + + " because this locale is already in use in an other translation of the same " + + ContentModel.TYPE_MULTILINGUAL_CONTAINER + "."); + } + + // get the locale of ML Container + Locale localMlContainer = (Locale) nodeService.getProperty( + mlContainer, + ContentModel.PROP_LOCALE); + + // if locale of the container is equals to the locale of + // the node (before update). The nodeRef is the pivot language + // and the locale of the mlContainer must be modified + if(localeBefore.equals(localMlContainer)) + { + nodeService.setProperty( + mlContainer, + ContentModel.PROP_LOCALE, + localeAfter); + } + + } + + // else no action to perform + } + +} diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java new file mode 100644 index 0000000000..bd9d58ed8e --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/MultilingualTestSuite.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.alfresco.repo.model.filefolder.FolderTypeTest; +import org.alfresco.repo.model.ml.tools.ContentFilterLanguagesMapTest; +import org.alfresco.repo.model.ml.tools.EmptyTranslationAspectTest; +import org.alfresco.repo.model.ml.tools.MLContainerTypeTest; +import org.alfresco.repo.model.ml.tools.MultilingualContentServiceImplTest; +import org.alfresco.repo.model.ml.tools.MultilingualDocumentAspectTest; + + +/** + * Multilingual test suite + * + * @author yanipig + */ +public class MultilingualTestSuite extends TestSuite +{ + /** + * Creates the test suite + * + * @return the test suite + */ + public static Test suite() + { + TestSuite suite = new TestSuite(); + + suite.addTestSuite(EmptyTranslationAspectTest.class); + suite.addTestSuite(ContentFilterLanguagesMapTest.class); + suite.addTestSuite(MultilingualContentServiceImplTest.class); + suite.addTestSuite(MultilingualDocumentAspectTest.class); + suite.addTestSuite(MLContainerTypeTest.class); + suite.addTestSuite(FolderTypeTest.class); + + return suite; + } +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java new file mode 100644 index 0000000000..efda4f070c --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * Base multilingual test cases + * + * @author yanipig + */ +public abstract class AbstractMultilingualTestCases extends TestCase +{ + + protected static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + protected ServiceRegistry serviceRegistry; + protected AuthenticationComponent authenticationComponent; + protected TransactionService transactionService; + protected NodeService nodeService; + protected FileFolderService fileFolderService; + protected VersionService versionService; + protected MultilingualContentService multilingualContentService; + protected NodeRef folderNodeRef; + protected ContentFilterLanguagesService contentFilterLanguagesService; + protected NodeArchiveService nodeArchiveService; + @Override + protected void setUp() throws Exception + { + nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService"); + serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + authenticationComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent"); + transactionService = serviceRegistry.getTransactionService(); + nodeService = serviceRegistry.getNodeService(); + fileFolderService = serviceRegistry.getFileFolderService(); + versionService = serviceRegistry.getVersionService(); + multilingualContentService = (MultilingualContentService) ctx.getBean("MultilingualContentService"); + contentFilterLanguagesService = (ContentFilterLanguagesService) ctx.getBean("ContentFilterLanguagesService"); + + // Run as admin + authenticationComponent.setCurrentUser("admin"); + + // Create a folder to work in + TransactionWork createFolderWork = new TransactionWork() + { + public NodeRef doWork() throws Exception + { + StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); + // Create the folder + NodeRef folderNodeRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), + ContentModel.TYPE_FOLDER).getChildRef(); + // done + return folderNodeRef; + } + }; + folderNodeRef = TransactionUtil.executeInUserTransaction(transactionService, createFolderWork); + } + + @Override + protected void tearDown() throws Exception + { + // Clear authentication + try + { + authenticationComponent.clearCurrentSecurityContext(); + } + catch (Throwable e) + { + e.printStackTrace(); + } + } + + protected NodeRef createContent() + { + NodeRef contentNodeRef = fileFolderService.create( + folderNodeRef, + "" + System.currentTimeMillis(), + ContentModel.TYPE_CONTENT).getNodeRef(); + // add some content + ContentWriter contentWriter = fileFolderService.getWriter(contentNodeRef); + contentWriter.putContent("ABC"); + // done + return contentNodeRef; + } + + public void testSetup() throws Exception + { + // Ensure that content can be created + createContent(); + } + +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java b/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java new file mode 100644 index 0000000000..1e576df048 --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * Content filter language service test cases + * + * @see org.alfresco.service.cmr.ml.ContentFilterLanguagesService + * @see org.alfresco.repo.model.ml.ContentFilterLanguagesMap + * + * @author yanipig + */ +public class ContentFilterLanguagesMapTest extends AbstractMultilingualTestCases +{ + + public void testGetFilterLanguages() throws Exception + { + // get the list of content filter languages + List lggs = contentFilterLanguagesService.getFilterLanguages(); + + // Ensure that the list is not null + assertNotNull("Language list is null", lggs); + + // Ensure that the list is read-only + try + { + lggs.add("NEW LOCALE"); + assertTrue("Add a value to the content filter language list is not permit, this list would be read only", false); + } + catch (Exception e) + { + // test case ok + } + + try + { + lggs.remove(0); + assertTrue("Remove a value to the content filter language list is not permit, this list would be read only", false); + } + catch (Exception e) + { + // test case ok + } + } + + @SuppressWarnings("unchecked") + public void testGetMissingLanguages() throws Exception + { + List lggs = contentFilterLanguagesService.getFilterLanguages(); + + // get missing languages with null parameter + List missingLggsNull = contentFilterLanguagesService.getMissingLanguages(null); + + // Ensure that the returned list is not null + assertNotNull("Language list returned with the null parameter is null", missingLggsNull); + // Ensure that the returned list is entire + assertEquals("Language list returned with the null parameter corrupted", missingLggsNull.size(), lggs.size()); + + // get missing languages with empty collection parameter + List missingLggsEmpty = contentFilterLanguagesService.getMissingLanguages(Collections.EMPTY_LIST); + + // Ensure that the returned list is not null + assertNotNull("Language list returned with the empty parameter is null", missingLggsEmpty); + // Ensure that the returned list is entire + assertEquals("Language list returned with the empty parameter corrupted", missingLggsEmpty.size(), lggs.size()); + + // get missing languages with a two locale list + List param = new ArrayList(); + param.add(0, lggs.get(0)); + param.add(1, lggs.get(1)); + List missingLggsOk = contentFilterLanguagesService.getMissingLanguages(param); + + // Ensure that the returned list is not null + assertNotNull("Language list returned with the correct parameter is null", missingLggsOk); + // Ensure that the returned list size is correct + assertEquals("Language list size returned with the correct parameter is not correct", missingLggsOk.size(), (lggs.size() - 2)); + // Ensure that the returned list don't content the preceding locales + assertFalse("Language found : " + param.get(0), missingLggsOk.contains(param.get(0))); + assertFalse("Language found : " + param.get(1), missingLggsOk.contains(param.get(1))); + // get missing languages with a not found locale + param.add(2, "WRONG LOCALE CODE"); + List missingLggsWrong = contentFilterLanguagesService.getMissingLanguages(param); + + // Ensure that the returned list is not null + assertNotNull("Language list returned with the wrong parameter is null", missingLggsWrong); + // Ensure that the returned list size is correct + assertEquals("Language list size returned with the correct parameter is not correct", missingLggsWrong.size(), (lggs.size() - 2)); + // Ensure that the returned list don't content the wrong locale + assertFalse("Language found : " + param.get(0), missingLggsWrong.contains(param.get(0))); + assertFalse("Language found : " + param.get(1), missingLggsWrong.contains(param.get(1))); + assertFalse("Language found : " + param.get(2), missingLggsWrong.contains(param.get(2))); + } + + public void testISOCodeConvertions() throws Exception + { + // New ISO code list + String[] newCode = {"he", "id", "yi"}; + String[] oldCode = {"iw", "in", "ji"}; + + Locale loc0 = new Locale(newCode[0]); + Locale loc1 = new Locale(newCode[1]); + Locale loc2 = new Locale(newCode[2]); + + // Ensure that java.util.Locale has converted the new ISO code into new iso code + assertEquals("java.util.Locale Convertion not correct for " + newCode[0], oldCode[0], loc0.getLanguage()); + assertEquals("java.util.Locale Convertion not correct for " + newCode[1], oldCode[1], loc1.getLanguage()); + assertEquals("java.util.Locale Convertion not correct for " + newCode[2], oldCode[2], loc2.getLanguage()); + + // Ensure that the convertion is correcte + assertEquals("Convertion of new ISO codes not correct for " + newCode[0], oldCode[0], contentFilterLanguagesService.convertToOldISOCode(newCode[0])); + assertEquals("Convertion of new ISO codes not correct for " + newCode[1], oldCode[1], contentFilterLanguagesService.convertToOldISOCode(newCode[1])); + assertEquals("Convertion of new ISO codes not correct for " + newCode[2], oldCode[2], contentFilterLanguagesService.convertToOldISOCode(newCode[2])); + + + assertEquals("Convertion of old ISO codes not correct for " + oldCode[0], newCode[0], contentFilterLanguagesService.convertToNewISOCode(oldCode[0])); + assertEquals("Convertion of old ISO codes not correct for " + oldCode[1], newCode[1], contentFilterLanguagesService.convertToNewISOCode(oldCode[1])); + assertEquals("Convertion of old ISO codes not correct for " + oldCode[2], newCode[2], contentFilterLanguagesService.convertToNewISOCode(oldCode[2])); + } +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java b/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java new file mode 100644 index 0000000000..af2f8f214e --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Empty translations aspect test cases + * + * @see org.alfresco.service.cmr.ml.EmptyTranslationAspect + * + * @author yanipig + */ +public class EmptyTranslationAspectTest extends AbstractMultilingualTestCases { + + protected ContentService contentService; + + protected void setUp() throws Exception + { + contentService = (ContentService) ctx.getBean("ContentService"); + super.setUp(); + } + + public void testCopy() throws Exception + { + NodeRef pivot = createContent(); + NodeRef empty = null; + + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + + boolean exceptionCatched = false; + NodeRef copy = null; + + try + { + copy = fileFolderService.copy( + empty, + nodeService.getPrimaryParent(empty).getParentRef(), + "copyOfEmpty" + System.currentTimeMillis()).getNodeRef(); + + // test failed + } + catch (Exception ignore) + { + exceptionCatched = true; + } + + // Ensure that the copy of an empty translation throws an exception + assertTrue("The copy of a translation must throws an exception", exceptionCatched); + // Ensure that the copy node is null + assertNull("The copy must fail ", copy); + } + + public void testDeleteNode() throws Exception + { + NodeRef pivot = createContent(); + NodeRef empty = null; + + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + + nodeService.deleteNode(empty); + + // Ensure that the empty translation is removed from the workspace + assertFalse("The empty translation must be removed from the wokspace", nodeService.exists(empty)); + // Ensure that the empty translation is not archived + assertFalse("The empty translation must be removed from the wokspace", nodeService.exists(nodeArchiveService.getArchivedNode(empty))); + } + + public void testGetContent() throws Exception + { + NodeRef pivot = createContent(); + NodeRef otherTranslation = createContent(); + + NodeRef empty = null; + + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + multilingualContentService.addTranslation(otherTranslation, pivot, Locale.KOREAN); + + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + + // Modify the content of the pivot + String contentString = fileFolderService.getReader(pivot).getContentString(); + contentString += "_TEST_"; + + fileFolderService.getWriter(pivot).putContent(contentString); + + // Ensure that the URL property of the empty translation content is null + assertNull("The URL property of an empty translation must be null", ((ContentData) nodeService.getProperty(empty, ContentModel.PROP_CONTENT)).getContentUrl()); + // Ensure that the content retourned by a get reader of the empty document is the same as the pivot + assertEquals("The content retourned of the empty translation must be the same that the content of the pivot", + fileFolderService.getReader(pivot).getContentString(), + fileFolderService.getReader(empty).getContentString()); + // Ensure that the content retourned by a get reader of the empty document is different to the non-pivot translation + assertNotSame("The content retourned of the empty translation must be different that the content of a non-pivot translation", + fileFolderService.getReader(otherTranslation).getContentString(), + fileFolderService.getReader(empty).getContentString()); + + + // modify the pivot + Map props = nodeService.getProperties(mlContainer); + props.put(ContentModel.PROP_LOCALE, Locale.KOREAN); + nodeService.setProperties(mlContainer, props); + + // Ensure that the modicfication of the pivot is take in account + assertEquals("The modification of the pivot is not take in account", + fileFolderService.getReader(otherTranslation).getContentString(), + fileFolderService.getReader(empty).getContentString()); + } + + public void testUpdateContent() throws Exception + { + NodeRef pivot = createContent(); + NodeRef empty = null; + + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + + // update the empty translation content + ContentWriter writer = contentService.getWriter(empty, ContentModel.PROP_CONTENT, true); + writer.setMimetype("text/plain"); + writer.putContent("ANY_CONTENT"); + + // Ensure that the URL property of the empty translation content is not null + assertNotNull("The url of an updated (ex)empty transation can't be null", ((ContentData) nodeService.getProperty(empty, ContentModel.PROP_CONTENT)).getContentUrl()); + // Ensure that the mlEmptyTranslation aspect is removed + assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " aspect of an updated (ex)empty translation must be removed", nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); + // Ensure that the mlDocument aspect is not removed + assertTrue("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect of an updated (ex)empty translation must be keeped", nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + // Ensure that the content is written + assertEquals("The content of the (ex)empty translation is not correct", + fileFolderService.getReader(empty).getContentString(), + "ANY_CONTENT"); + // Ensure that the content is different that the content of the pivot + assertNotSame("The content of the (ex)empty translation is not updated and is the same as the content of the pivot", + fileFolderService.getReader(empty).getContentString(), + fileFolderService.getReader(pivot).getContentString()); + } + + public void testRemoveAspect() throws Exception + { + NodeRef pivot = createContent(); + NodeRef empty = null; + + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + + // 1. remove mlEmptyTranslation aspect with empty content + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + + // Ensure that the empty translation is deleted + assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with no content the node must be deleted", + nodeService.exists(empty)); + assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with no content the node must be deleted and can't be archived", + nodeService.exists(nodeArchiveService.getArchivedNode(empty))); + + // 2. remove mlDocument aspect with empty content + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT); + + // Ensure that the empty translation is deleted + assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " of an empty translation with no content the node must be deleted", + nodeService.exists(empty)); + assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " of an empty translation with no content the node must be deleted and can't be archived", + nodeService.exists(nodeArchiveService.getArchivedNode(empty))); + + // 3. remove mlEmptyTranslation aspect with not empty content + empty = multilingualContentService.addEmptyTranslation(pivot, "empty_" + System.currentTimeMillis(), Locale.CHINESE); + ContentWriter writer = contentService.getWriter(empty, ContentModel.PROP_CONTENT, true); + writer.setMimetype("text/plain"); + writer.putContent("ANY_CONTENT"); + nodeService.removeAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION); + + // Ensure that the empty translation is NOT deleted + assertTrue("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content the node must NOT be deleted", + nodeService.exists(empty)); + // Ensure that the mlEmptyTranslation aspect is removed + assertFalse("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content this aspect must be removed", + nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); + // Ensure that the mlEmptyDocument in NOT removed + assertTrue("After removed the " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " of an empty translation with content the " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect must be keeped", + nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + } + +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java b/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java new file mode 100644 index 0000000000..7e2b1fa13c --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Multilingual container type test cases + * + * @see org.alfresco.service.cmr.ml.MLContainerType + * + * @author yanipig + */ +public class MLContainerTypeTest extends AbstractMultilingualTestCases { + + + public void testDeleteNode() throws Exception + { + NodeRef pivot = createContent(); + NodeRef empty; + + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + empty = multilingualContentService.addEmptyTranslation(pivot, "Empty_" + System.currentTimeMillis(), Locale.ENGLISH); + + nodeService.deleteNode(mlContainer); + + // Ensure that the mlContainer is deleted + assertFalse("The mlContainer must be deleted", nodeService.exists(mlContainer)); + // Ensure that the empty translation is deleted + assertFalse("The mlEmptyTranslation must be deleted", nodeService.exists(empty)); + // Ensure that the non-empty document is not deleted + assertTrue("The mlDocument must not be deleted", nodeService.exists(pivot)); + // Ensure that the mlDocument property of the non-empty document is removed + assertFalse("The " + ContentModel.ASPECT_MULTILINGUAL_DOCUMENT + " aspect of the mlDocument must be removed", + nodeService.hasAspect(pivot, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + } + + @SuppressWarnings("unused") + public void testEditLocale() throws Exception + { + NodeRef trans1 = createContent(); + NodeRef trans2 = createContent(); + NodeRef trans3 = createContent(); + NodeRef empty = null; + + NodeRef mlContainer = multilingualContentService.makeTranslation(trans1, Locale.FRENCH); + multilingualContentService.addTranslation(trans2, trans1, Locale.GERMAN); + multilingualContentService.addTranslation(trans3, trans1, Locale.ITALIAN); + empty = multilingualContentService.addEmptyTranslation(trans1, "EMPTY_" + System.currentTimeMillis(), Locale.JAPANESE); + + // 1. Locale as null + + // Ensure that the setting of the locale of the mlContainer as null throws an excpetion + assertTrue("The setting of the locale of a mlContainer must throws an exception", + setLocaleProp(mlContainer, null)); + // Ensure that the locale of the mlContainer is not changed + assertEquals("The locale of the mlContainer would not be changed", + Locale.FRENCH, + nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + + // 2. Set an non-existing locale + + // Ensure that the setting of the locale of the mlContainer as a non-existing translation language throws an excpetion + assertTrue("The setting of the locale of a mlContainer as a non-existing translation language must throws an exception", + setLocaleProp(mlContainer, Locale.US)); + // Ensure that the locale of the mlContainer is not changed + assertEquals("The locale of the mlContainer would not be changed", + Locale.FRENCH, + nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + + // 3. Set the locale of a empty translation + + // Ensure that the setting of the locale of the mlContainer as an empty translation language throws an excpetion + assertTrue("The setting of the locale of a mlContainer as an empty translation language must throws an exception", + setLocaleProp(mlContainer, Locale.JAPANESE)); + // Ensure that the locale of the mlContainer is not changed + assertEquals("The locale of the mlContainer would not be changed", + Locale.FRENCH, + nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + + // 4. Set an existing and valid locale + + // Ensure that the setting of the locale of the mlContainer as an existing and a non-empty translation DOESN'T throw an excpetion + assertFalse("The setting of the locale of a mlContainer as an existing and a non-empty translation DOESN'T throw an excpetion", + setLocaleProp(mlContainer, Locale.ITALIAN)); + // Ensure that the locale of the mlContainer is not changed + assertEquals("The locale of the mlContainer would be changed", + Locale.ITALIAN, + nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + + } + + + + + private boolean setLocaleProp(NodeRef node, Locale locale) throws Exception + { + Map props = nodeService.getProperties(node); + props.put(ContentModel.PROP_LOCALE, locale); + + boolean exceptionCatched = false; + + try + { + nodeService.setProperties(node, props); + + } + catch (IllegalArgumentException ignore) + { + exceptionCatched = true; + } + catch(Exception ex) + { + throw new Exception(ex); + } + + + return exceptionCatched; + } +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java b/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java new file mode 100644 index 0000000000..68153ee3ec --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; + + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; + +/** + * @see org.alfresco.repo.ml.MultilingualContentServiceImpl + * + * @author Derek Hulley + * @author Philippe Dubois + */ +public class MultilingualContentServiceImplTest extends AbstractMultilingualTestCases +{ + + public void testMakeTranslation() throws Exception + { + NodeRef contentNodeRef = createContent(); + // Turn the content into a translation with the appropriate structures + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(contentNodeRef, Locale.CHINESE); + // Check it + assertNotNull("Container not created", mlContainerNodeRef); + // Check the container child count + assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size()); + } + + public void testAddTranslationUsingContainer() throws Exception + { + // Make a container with a single translation + NodeRef chineseContentNodeRef = createContent(); + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + // Create some more content + NodeRef frenchContentNodeRef = createContent(); + // Make this a translation of the Chinese + NodeRef newMLContainerNodeRef = multilingualContentService.addTranslation( + frenchContentNodeRef, + mlContainerNodeRef, + Locale.FRENCH); + // Make sure that the original container was used + assertEquals("Existing container should have been used", mlContainerNodeRef, newMLContainerNodeRef); + // Check the container child count + assertEquals("Incorrect number of child nodes", 2, nodeService.getChildAssocs(mlContainerNodeRef).size()); + } + + public void testAddTranslationUsingContent() throws Exception + { + // Make a container with a single translation + NodeRef chineseContentNodeRef = createContent(); + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + // Create some more content + NodeRef frenchContentNodeRef = createContent(); + // Make this a translation of the Chinese + NodeRef newMLContainerNodeRef = multilingualContentService.addTranslation( + frenchContentNodeRef, + chineseContentNodeRef, + Locale.FRENCH); + // Make sure that the original container was used + assertEquals("Existing container should have been used", mlContainerNodeRef, newMLContainerNodeRef); + // Check the container child count + assertEquals("Incorrect number of child nodes", 2, nodeService.getChildAssocs(mlContainerNodeRef).size()); + } + + @SuppressWarnings("unused") + public void testGetMissingTranslation() throws Exception + { + List langList = contentFilterLanguagesService.getFilterLanguages(); + int langListSize = langList.size(); + + // make sure that it exists at least tree language filter + assertFalse("The testGetMissingTranslation test case needs at least three language", langListSize < 3); + + // get the first tree locale of the content filter language list + Locale loc1 = I18NUtil.parseLocale(langList.get(0)); + Locale loc2 = I18NUtil.parseLocale(langList.get(1)); + Locale loc3 = I18NUtil.parseLocale(langList.get(2)); + + // create three content + NodeRef nodeRef1 = createContent(); + NodeRef nodeRef2 = createContent(); + NodeRef nodeRef3 = createContent(); + + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(nodeRef1, loc1); + + List missing = multilingualContentService.getMissingTranslations(mlContainerNodeRef, false); + + // make sure that the missing language list size is correct + assertFalse("Missing Translation Size false. " + + "Real size : " + missing.size() + ". Normal Size " + (langListSize - 1), missing.size() != (langListSize - 1)); + + // make sure that the missing language list is correct + assertFalse("Missing Translation List false. Locale " + loc1 + " found", missing.contains(loc1.toString())); + + multilingualContentService.addTranslation(nodeRef2, mlContainerNodeRef, loc2); + multilingualContentService.addTranslation(nodeRef3, mlContainerNodeRef, loc3); + + + missing = multilingualContentService.getMissingTranslations(mlContainerNodeRef, false); + + // make sure that the missing language list size is correct + assertFalse("Missing Translation Size false. " + + "Real size : " + missing.size() + ". Normal Size " + (langListSize - 3), missing.size() != (langListSize - 3)); + + // make sure that the missing language list is correct + assertFalse("Missing Translation List false. Locale " + loc2 + " or " + loc3 + " found", missing.contains(loc2.toString()) || missing.contains(loc3.toString())); + } + + @SuppressWarnings("unused") + public void testPivotTranslation() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + + // make sure that the pivot language is set + assertNotNull("Pivot language not set", nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE)); + + // make sure that the pivot language is correctly set + assertTrue("Pivot language not correctly set", nodeService.getProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE).equals(Locale.CHINESE)); + + NodeRef frenchContentNodeRef = createContent(); + multilingualContentService.addTranslation(frenchContentNodeRef, chineseContentNodeRef, Locale.FRENCH); + + // make sure that the pivot noderef is correct + assertTrue("Pivot node ref not correct", multilingualContentService.getPivotTranslation(mlContainerNodeRef).equals(chineseContentNodeRef)); + + // modify the pivot language + nodeService.setProperty(mlContainerNodeRef, ContentModel.PROP_LOCALE, Locale.FRENCH); + + // make sure that the modified pivot noderef is correct + assertTrue("Pivot node ref not correct", multilingualContentService.getPivotTranslation(mlContainerNodeRef).equals(frenchContentNodeRef)); + + } + + @SuppressWarnings("unused") + public void testCreateEmptyTranslation() throws Exception + { + NodeRef chineseContentNodeRef = createContent(); + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + + NodeRef empty = multilingualContentService.addEmptyTranslation(chineseContentNodeRef, "" + System.currentTimeMillis(), Locale.CANADA); + + // Ensure that the empty translation is not null + assertNotNull("The creation of the empty document failed ", empty); + // Ensure that the empty translation has the mlDocument aspect + assertTrue("The empty document must have the mlDocument aspect", nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + // Ensure that the empty translation has the mlEmptyTranslation aspect + assertTrue("The empty document must have the mlEmptyTranslation aspect", nodeService.hasAspect(empty, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)); + } + + @SuppressWarnings("unused") + public void testCreateEdition() throws Exception + { + // Make some content + NodeRef chineseContentNodeRef = createContent(); + NodeRef frenchContentNodeRef = createContent(); + NodeRef japaneseContentNodeRef = createContent(); + // Add to container + NodeRef mlContainerNodeRef = multilingualContentService.makeTranslation(chineseContentNodeRef, Locale.CHINESE); + multilingualContentService.addTranslation(frenchContentNodeRef, mlContainerNodeRef, Locale.FRENCH); + multilingualContentService.addTranslation(japaneseContentNodeRef, mlContainerNodeRef, Locale.JAPANESE); + // Check the container child count + assertEquals("Incorrect number of child nodes", 3, nodeService.getChildAssocs(mlContainerNodeRef).size()); + + // Version each of the documents + List nodeRefs = new ArrayList(3); + nodeRefs.add(chineseContentNodeRef); + nodeRefs.add(frenchContentNodeRef); + nodeRefs.add(japaneseContentNodeRef); + versionService.createVersion(nodeRefs, null); + // Get the current versions of each of the documents + Version chineseVersionPreEdition = versionService.getCurrentVersion(chineseContentNodeRef); + Version frenchVersionPreEdition = versionService.getCurrentVersion(frenchContentNodeRef); + Version japaneseVersionPreEdition = versionService.getCurrentVersion(japaneseContentNodeRef); + + // Create the edition, keeping the Chinese translation as the basis + multilingualContentService.createEdition(chineseContentNodeRef); + // Check the container child count + assertEquals("Incorrect number of child nodes", 1, nodeService.getChildAssocs(mlContainerNodeRef).size()); + + // Get the document versions now + Version chineseVersionPostEdition = versionService.getCurrentVersion(chineseContentNodeRef); + assertFalse("Expected document to be gone", nodeService.exists(frenchContentNodeRef)); + assertFalse("Expected document to be gone", nodeService.exists(japaneseContentNodeRef)); + + // Now be sure that we can get the required information using the version service + VersionHistory mlContainerVersionHistory = versionService.getVersionHistory(mlContainerNodeRef); + Collection mlContainerVersions = mlContainerVersionHistory.getAllVersions(); + // Loop through and get all the children of each version + for (Version mlContainerVersion : mlContainerVersions) + { + NodeRef versionedMLContainerNodeRef = mlContainerVersion.getFrozenStateNodeRef(); + // Get all the children + Map translationsByLocale = multilingualContentService.getTranslations( + versionedMLContainerNodeRef); + // Count the children + int count = translationsByLocale.size(); + } + } +} diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java new file mode 100644 index 0000000000..d7b4a70148 --- /dev/null +++ b/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.model.ml.tools; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Multilingual document aspect test cases + * + * @see org.alfresco.service.cmr.ml.MultilingualDocumentAspect + * + * @author yanipig + */ +public class MultilingualDocumentAspectTest extends AbstractMultilingualTestCases +{ + public void testCopy() throws Exception + { + NodeRef original = createContent(); + NodeRef mlContainer = multilingualContentService.makeTranslation(original, Locale.FRENCH); + + NodeRef copy = + fileFolderService.copy(original, nodeService.getPrimaryParent(original).getParentRef(), "COPY" + System.currentTimeMillis()).getNodeRef(); + + // Ensure that the copy removes the mlDocument aspect + assertFalse("The copy of a mlDocument can't have the multilingual aspect", nodeService.hasAspect(copy, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + + // Ensure that the copy removes the association between the mlConatiner and the new node + assertEquals("The copy of a mlDocument can't be a children of the mlContainer", 1, multilingualContentService.getTranslations(mlContainer).size()); + + // Ensure that the copy removes the Locale property of the new node + assertNull("The copy of a mlDocument can't keep the locale property", nodeService.getProperty(copy, ContentModel.PROP_LOCALE)); + } + + public void testDeleteNode() throws Exception + { + NodeRef trad1 = createContent(); + NodeRef trad2 = createContent(); + NodeRef trad3 = createContent(); + + NodeRef parent = nodeService.getPrimaryParent(trad1).getParentRef(); + + NodeRef mlContainer = multilingualContentService.makeTranslation(trad1, Locale.FRENCH); + multilingualContentService.addTranslation(trad2, trad1, Locale.GERMAN); + multilingualContentService.addTranslation(trad3, trad1, Locale.ITALIAN); + + nodeService.deleteNode(trad3); + + // Ensure that the deleted node is romoved from its space + assertEquals("The deleted node must be removed to the space", 2, nodeService.getChildAssocs(parent).size()); + // Ensure that the mlContainer doesn't keep an association to the deleted node + assertEquals("The deleted node must be removed to the child associations of the mlContainer", 2, multilingualContentService.getTranslations(mlContainer).size()); + + // retore the deleted node + NodeRef restoredNode = nodeArchiveService.restoreArchivedNode(nodeArchiveService.getArchivedNode(trad3)).getRestoredNodeRef(); + + // Ensure that the restored node is restored to it s original space + assertEquals("The restaured node must be restaured to the the space", 3, nodeService.getChildAssocs(parent).size()); + // Ensure that the restored node is not linked to the mlContainer + assertEquals("The restaured node would not be restaured to the mlContainer", 2, multilingualContentService.getTranslations(mlContainer).size()); + // Ensure that the restored node doesn't keep the mlDocument aspect + assertFalse("The restaured node can't keep the multilingual aspect", nodeService.hasAspect(restoredNode, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + // Ensure that the restored node doesn't keep the locale property + assertNull("The restaured node can't keep the locale property", nodeService.getProperty(restoredNode, ContentModel.PROP_LOCALE)); + + + } + + public void testDeletePivot() throws Exception + { + NodeRef pivot = createContent(); + NodeRef trans1 = createContent(); + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); + + //nodeService.deleteNode(trans1); + nodeService.deleteNode(pivot); + + // Ensure that pivot is removed + assertFalse("The pivot would be removed", nodeService.exists(pivot)); + // Ensure that the mlContainer is removed + assertFalse("The mlContainer must be removed if the pivot is removed", nodeService.exists(mlContainer)); + // Ensure that trans1 is NOT removed + assertTrue("The last translation would not be removed", nodeService.exists(trans1)); + // Ensure that trans1 has no mlDocument aspect + assertFalse("The last translation can't keep the multilingual aspect", nodeService.hasAspect(trans1, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)); + // Ensure that trans1 has no locale propety + assertNull("The last translation can't keep the locale property", nodeService.getProperty(trans1, ContentModel.PROP_LOCALE)); + } + + public void testDeleteLastNode() throws Exception + { + NodeRef pivot = createContent(); + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + + nodeService.deleteNode(pivot); + + // Ensure that the mlContainer is removed too + assertFalse("The mlContainer must be removed if the last translation is removed", nodeService.exists(mlContainer)); + + } + + public void testRemoveAspect() throws Exception + { + // entierly covered by the delete tests + } + + public void testUpdateLocale() throws Exception + { + NodeRef pivot = createContent(); + NodeRef trans1 = createContent(); + NodeRef mlContainer = multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); + + // modify the locale for the translation + Map props = nodeService.getProperties(trans1); + props.put(ContentModel.PROP_LOCALE, Locale.GERMAN); + nodeService.setProperties(trans1, props); + + // Ensure that the pivot reference is not changed for the mlContainer and the locale is changed for the translation + assertEquals("The locale for the pivot would be changed ",Locale.GERMAN, nodeService.getProperty(trans1, ContentModel.PROP_LOCALE)); + assertEquals("The pivot reference would not be changed in the mlContainer", Locale.FRENCH, nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + + // modify the locale for the pivot + props = nodeService.getProperties(pivot); + props.put(ContentModel.PROP_LOCALE, Locale.US); + nodeService.setProperties(pivot, props); + + // Ensure that the pivot reference is changed (in the pivot and in the mlContainer) + assertEquals("The locale for the pivot would be changed ", Locale.US, nodeService.getProperty(pivot, ContentModel.PROP_LOCALE)); + assertEquals("The pivot reference would be changes in the mlContainer", Locale.US, nodeService.getProperty(mlContainer, ContentModel.PROP_LOCALE)); + } + + public void testUpdateRedundantLocale() throws Exception + { + NodeRef pivot = createContent(); + NodeRef trans1 = createContent(); + NodeRef trans2 = createContent(); + + multilingualContentService.makeTranslation(pivot, Locale.FRENCH); + multilingualContentService.addTranslation(trans1, pivot, Locale.KOREAN); + multilingualContentService.addTranslation(trans2, pivot, Locale.JAPANESE); + + // 1. Try with redundant locale + + // modify the locale for the translation 2 + Map props = nodeService.getProperties(trans2); + props.put(ContentModel.PROP_LOCALE, Locale.KOREAN); + + boolean exceptionCatched = false; + + try + { + nodeService.setProperties(trans2, props); + // test failed + } catch (Exception ignore) + { + exceptionCatched = true; + } + + // Ensure that the the exception was catched. + assertTrue("The modification of this locale must catch an exception because it is already in use in another translation", exceptionCatched); + // Ensure that the locale of the trans2 is unchanged + assertEquals("The locale must not be changed", + Locale.JAPANESE, + (Locale) nodeService.getProperty(trans2, ContentModel.PROP_LOCALE)); + + // 2. Try with a non-redundant locale + + props = nodeService.getProperties(trans2); + props.put(ContentModel.PROP_LOCALE, Locale.ITALIAN); + + exceptionCatched = false; + + try + { + nodeService.setProperties(trans2, props); + + } catch (Exception ignore) + { + // test failed + exceptionCatched = true; + } + + // Ensure that the exception was not catched + assertFalse("The modification of the locale would not throws an exception", exceptionCatched); + // Ensure that the locale is modified + assertEquals("The locale must be changed", + Locale.ITALIAN, + (Locale) nodeService.getProperty(trans2, ContentModel.PROP_LOCALE)); + } +} + diff --git a/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java b/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java new file mode 100644 index 0000000000..24974c84db --- /dev/null +++ b/source/java/org/alfresco/repo/node/MLTranslationInterceptor.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.ml.MultilingualContentService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Interceptor to + * - filter the multilingual nodes to display the documents in the prefered language of teh user + * + * @author yanipig + */ +public class MLTranslationInterceptor implements MethodInterceptor +{ + private static Log logger = LogFactory + .getLog(MLTranslationInterceptor.class); + + private NodeService nodeService; + + private MultilingualContentService multilingualContentService; + + @SuppressWarnings("unchecked") + public Object invoke(MethodInvocation invocation) throws Throwable + { + Object ret = null; + String methodName = invocation.getMethod().getName(); + + // intercept the methods getChildAssocs and getChildByNames to apply filter. + if (methodName.equals("getChildAssocs") || methodName.equals("getChildByName")) + { + ret = invocation.proceed(); + + NodeRef parent = (NodeRef) invocation.getArguments()[0]; + + // all the association returned by the method + List allChildAssoc = (List) ret; + + // get the user content filter language + Locale filterLocale = I18NUtil.getContentLocaleOrNull(); + + if(filterLocale != null + && nodeService.getType(parent).equals(ContentModel.TYPE_FOLDER) + && ret != null + && !allChildAssoc.isEmpty() + ) + { + + // the list of Association to return + List toReturn = new ArrayList(); + // the ml containers found in the folder + List mlContainers = new ArrayList(); + + // construct the list of ML Container + for (ChildAssociationRef assoc : allChildAssoc) + { + NodeRef child = assoc.getChildRef(); + + QName type = nodeService.getType(child); + + if(type.equals(ContentModel.TYPE_CONTENT) && + nodeService.hasAspect(child, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) + { + NodeRef container = multilingualContentService.getTranslationContainer(child); + + if (!mlContainers.contains(container)) + { + mlContainers.add(container); + } + } + else + { + // no specific treatment for folder and non-multilingual document + toReturn.add(assoc); + } + } + + // for each mlContainer found, choose the unique document to return + for(NodeRef container : mlContainers) + { + // get each translation language + Set locales = multilingualContentService.getTranslations(container).keySet(); + + if(locales != null && locales.size() > 0) + { + Locale matchedLocal = I18NUtil.getNearestLocale(filterLocale, locales); + + NodeRef matchedTranslation = null; + + // if the filter language is not found + if(matchedLocal == null) + { + // get the pivot translation + matchedTranslation = multilingualContentService.getPivotTranslation(container); + } + else + { + // get the matched translation + matchedTranslation = multilingualContentService.getTranslations(container).get(matchedLocal); + } + + toReturn.add(new ChildAssociationRef(null, null, null, matchedTranslation)); + } + } + + ret = toReturn; + + if (logger.isDebugEnabled()) + { + logger.debug("Filter has found " + + allChildAssoc.size() + " entries, " + + mlContainers.size() + " different ML Container " + + "and returns " + toReturn.size() + " nodes"); + } + } + + } else + { + ret = invocation.proceed(); + } + + return ret; + } + + public MultilingualContentService getMultilingualContentService() + { + return multilingualContentService; + } + + public void setMultilingualContentService( + MultilingualContentService multilingualContentService) + { + this.multilingualContentService = multilingualContentService; + } + + public NodeService getNodeService() + { + return nodeService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index b3e15f84a4..c317446cba 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -37,6 +37,7 @@ import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.CopyService; @@ -255,7 +256,7 @@ public class ServiceDescriptorRegistry */ public ActionService getActionService() { - return (ActionService)getService(ACTION_SERVICE); + return (ActionService)getService(ACTION_SERVICE); } /* (non-Javadoc) @@ -364,6 +365,14 @@ public class ServiceDescriptorRegistry return (AttributeService)getService(ATTRIBUTE_SERVICE); } + /* (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getContentFilterLanguagesService() + */ + public ContentFilterLanguagesService getContentFilterLanguagesService() + { + return (ContentFilterLanguagesService) getService(CONTENT_FILTER_LANGUAGES_SERVICE); + } + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getAVMLockingService() */ diff --git a/source/java/org/alfresco/repo/version/VersionModel.java b/source/java/org/alfresco/repo/version/VersionModel.java index f3ebcb7bd9..b28b74a38b 100644 --- a/source/java/org/alfresco/repo/version/VersionModel.java +++ b/source/java/org/alfresco/repo/version/VersionModel.java @@ -15,11 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.version; @@ -29,9 +29,9 @@ import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.QName; /** - * interface conating the constants used by the light weight + * interface conating the constants used by the light weight * version store implementation - * + * * @author Roy Wetherall */ public interface VersionModel @@ -40,20 +40,20 @@ public interface VersionModel * Namespace */ public static final String NAMESPACE_URI = "http://www.alfresco.org/model/versionstore/1.0"; - - /** + + /** * The store protocol */ public static final String STORE_PROTOCOL = VersionService.VERSION_STORE_PROTOCOL; - + /** * The store id */ public static final String STORE_ID = "lightWeightVersionStore"; - - + + public static final String PROP_VERSION_LABEL = "versionLabel"; - public static final String PROP_CREATED_DATE = ContentModel.PROP_CREATED.getLocalName(); + public static final String PROP_CREATED_DATE = ContentModel.PROP_CREATED.getLocalName(); public static final String PROP_CREATOR = ContentModel.PROP_CREATOR.getLocalName(); public static final String PROP_VERSION_TYPE = "versionType"; public static final String PROP_VERSION_NUMBER = "versionNumber"; @@ -62,29 +62,29 @@ public interface VersionModel public static final String PROP_FROZEN_NODE_STORE_PROTOCOL = "frozenNodeStoreProtocol"; public static final String PROP_FROZEN_NODE_STORE_ID = "frozenNodeStoreId"; public static final String PROP_FROZEN_ASPECTS = "frozenAspects"; - + /** The version store root aspect */ public static final QName ASPECT_VERSION_STORE_ROOT = QName.createQName(NAMESPACE_URI, "versionStoreRoot"); - + /** * Version history type */ public static final String TYPE_VERSION_HISTORY = "versionHistory"; public static final QName TYPE_QNAME_VERSION_HISTORY = QName.createQName(NAMESPACE_URI, TYPE_VERSION_HISTORY); - + /** * Version history properties and associations */ public static final String PROP_VERSIONED_NODE_ID = "versionedNodeId"; - public static final QName PROP_QNAME_VERSIONED_NODE_ID = QName.createQName(NAMESPACE_URI, PROP_VERSIONED_NODE_ID); + public static final QName PROP_QNAME_VERSIONED_NODE_ID = QName.createQName(NAMESPACE_URI, PROP_VERSIONED_NODE_ID); public static final QName ASSOC_ROOT_VERSION = QName.createQName(NAMESPACE_URI, "rootVersion"); - + /** * Verison type */ public static final String TYPE_VERSION = "version"; public static final QName TYPE_QNAME_VERSION = QName.createQName(NAMESPACE_URI, TYPE_VERSION); - + /** * Version type properties and associations */ @@ -95,14 +95,14 @@ public interface VersionModel public static final QName PROP_QNAME_FROZEN_NODE_STORE_PROTOCOL = QName.createQName(NAMESPACE_URI, PROP_FROZEN_NODE_STORE_PROTOCOL); public static final QName PROP_QNAME_FROZEN_NODE_STORE_ID = QName.createQName(NAMESPACE_URI, PROP_FROZEN_NODE_STORE_ID); public static final QName PROP_QNAME_FROZEN_ASPECTS = QName.createQName(NAMESPACE_URI, PROP_FROZEN_ASPECTS); - public static final QName ASSOC_SUCCESSOR = QName.createQName(NAMESPACE_URI, "successor"); - + public static final QName ASSOC_SUCCESSOR = QName.createQName(NAMESPACE_URI, "successor"); + /** * Version Meta Data Value type */ public static final String TYPE_VERSION_META_DATA_VALUE = "versionMetaDataValue"; public static final QName TYPE_QNAME_VERSION_META_DATA_VALUE = QName.createQName(NAMESPACE_URI, TYPE_VERSION_META_DATA_VALUE); - + /** * Version Meta Data Value attributes */ @@ -110,13 +110,13 @@ public interface VersionModel public static final QName PROP_QNAME_META_DATA_NAME = QName.createQName(NAMESPACE_URI, PROP_META_DATA_NAME); public static final String PROP_META_DATA_VALUE = "metaDataValue"; public static final QName PROP_QNAME_META_DATA_VALUE = QName.createQName(NAMESPACE_URI, PROP_META_DATA_VALUE); - + /** * Versioned attribute type */ public static final String TYPE_VERSIONED_PROPERTY = "versionedProperty"; public static final QName TYPE_QNAME_VERSIONED_PROPERTY = QName.createQName(NAMESPACE_URI, TYPE_VERSIONED_PROPERTY); - + /** * Versioned attribute properties */ @@ -128,13 +128,13 @@ public interface VersionModel public static final QName PROP_QNAME_VALUE = QName.createQName(NAMESPACE_URI, PROP_VALUE); public static final QName PROP_QNAME_MULTI_VALUE = QName.createQName(NAMESPACE_URI, PROP_MULTI_VALUE); public static final QName PROP_QNAME_IS_MULTI_VALUE = QName.createQName(NAMESPACE_URI, PROP_IS_MULTI_VALUE); - + /** * Versioned child assoc type */ public static final String TYPE_VERSIONED_CHILD_ASSOC = "versionedChildAssoc"; public static final QName TYPE_QNAME_VERSIONED_CHILD_ASSOC = QName.createQName(NAMESPACE_URI, TYPE_VERSIONED_CHILD_ASSOC); - + /** * Versioned child assoc properties */ @@ -146,13 +146,13 @@ public interface VersionModel public static final QName PROP_QNAME_ASSOC_TYPE_QNAME = QName.createQName(NAMESPACE_URI, PROP_ASSOC_TYPE_QNAME); public static final QName PROP_QNAME_IS_PRIMARY = QName.createQName(NAMESPACE_URI, PROP_IS_PRIMARY); public static final QName PROP_QNAME_NTH_SIBLING = QName.createQName(NAMESPACE_URI, PROP_NTH_SIBLING); - + /** * Versioned assoc type */ public static final String TYPE_VERSIONED_ASSOC = "versionedAssoc"; public static final QName TYPE_QNAME_VERSIONED_ASSOC = QName.createQName(NAMESPACE_URI, TYPE_VERSIONED_ASSOC); - + /** * Child relationship names */ @@ -162,11 +162,18 @@ public interface VersionModel public static final String CHILD_VERSIONED_CHILD_ASSOCS = "versionedChildAssocs"; public static final String CHILD_VERSIONED_ASSOCS = "versionedAssocs"; public static final String CHILD_VERSION_META_DATA = "versionMetaData"; - + public static final QName CHILD_QNAME_VERSION_HISTORIES = QName.createQName(NAMESPACE_URI, CHILD_VERSION_HISTORIES); public static final QName CHILD_QNAME_VERSIONS = QName.createQName(NAMESPACE_URI, CHILD_VERSIONS); public static final QName CHILD_QNAME_VERSIONED_ATTRIBUTES = QName.createQName(NAMESPACE_URI, CHILD_VERSIONED_ATTRIBUTES); public static final QName CHILD_QNAME_VERSIONED_CHILD_ASSOCS = QName.createQName(NAMESPACE_URI, CHILD_VERSIONED_CHILD_ASSOCS); public static final QName CHILD_QNAME_VERSIONED_ASSOCS = QName.createQName(NAMESPACE_URI, CHILD_VERSIONED_ASSOCS); public static final QName CHILD_QNAME_VERSION_META_DATA = QName.createQName(NAMESPACE_URI, CHILD_VERSION_META_DATA); + + /** + * Created version associated to the deleted translations of an mlContainer + */ + public static final String PROP_TRANSLATION_VERIONS = "translationVersions"; + public static final QName PROP_QNAME_TRANSLATION_VERIONS = QName.createQName(VersionModel.NAMESPACE_URI, PROP_TRANSLATION_VERIONS); + } diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index aa4323927e..7370373cdf 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -36,6 +36,7 @@ import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.ml.ContentFilterLanguagesService; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.CopyService; @@ -85,6 +86,7 @@ public interface ServiceRegistry static final QName NODE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeService"); static final QName CONTENT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentService"); static final QName MIMETYPE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "MimetypeService"); + static final QName CONTENT_FILTER_LANGUAGES_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ContentFilterLanguagesService"); static final QName SEARCH_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "SearchService"); static final QName CATEGORY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CategoryService"); static final QName COPY_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "CopyService"); @@ -179,6 +181,12 @@ public interface ServiceRegistry @NotAuditable MimetypeService getMimetypeService(); + /** + * @return the content filter languages service (or null, if one is not provided) + */ + @NotAuditable + ContentFilterLanguagesService getContentFilterLanguagesService(); + /** * @return the search service (or null, if one is not provided) */ diff --git a/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java b/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java new file mode 100644 index 0000000000..f7c76bc102 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/ml/ContentFilterLanguagesService.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.service.cmr.ml; + +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.Auditable; +import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; + + +/** + * This service interface provides support for content filter languages . + * + * @author yanipig + * + */ +@PublicService +public interface ContentFilterLanguagesService +{ + + /** + * I18N message prefix to found the translation of a language label with a + * given lang code. + */ + public static final String MESSAGE_PREFIX = "content_filter_lang."; + + /** + * Get the order of the specified language code + * + * @param code + * @return + * @throws AlfrescoRuntimeException if the code doesn't exist + */ + @NotAuditable + public int getOrderByCode(String code); + + /** + * Get the language of the specified language code + * + * @param code + * @return + * @throws AlfrescoRuntimeException if the code doesn't exist + */ + @NotAuditable + public String getLabelByCode(String code); + + /** + * Get ordered list of languages code + * + * @return the map of displays indexed by extension + */ + @Auditable + public List getFilterLanguages(); + + /** + * Get the the odered filter which results form an extract of availableLanguages on the filterLanguages + * + * @param availableLanguages the languages list whose will be removed from the filterLanguages + * @return + */ + @Auditable + public List getMissingLanguages(List availableLanguages); + + /** + * @return the default content filter language, null if it's not set. + */ + @Auditable + public String getDefaultLanguage(); + + /** + * Since java.util.Locale uses and returns old ISO code and the content-filter-lang.xml + * respects the new ones. This method convert new codes into old codes: + *

(he, yi, and id) new codes to (iw, ji, and in) old codes

+ * + * @param code the ISO language code to convert + * @return the convertion of the codes he, yi, and id or the given code + */ + @NotAuditable + public String convertToOldISOCode(String code); + + /** + * Since java.util.Locale uses and returns old ISO code and the content-filter-lang.xml + * respects the new ones. This method convert old codes into new codes: + *

(iw, ji, and in) old codes to (he, yi, and id) new codes

+ * + * @param code the ISO language code to convert + * @return the convertion of the codes iw, ji, and in or the given code + */ + @NotAuditable + public String convertToNewISOCode(String code); +} diff --git a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java index f582e7fb18..ae94351f6f 100644 --- a/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java +++ b/source/java/org/alfresco/service/cmr/ml/MultilingualContentService.java @@ -24,6 +24,7 @@ */ package org.alfresco.service.cmr.ml; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -117,4 +118,38 @@ public interface MultilingualContentService */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationNodeRef", "locale"}) NodeRef getTranslationForLocale(NodeRef translationNodeRef, Locale locale); + + + /** + * Given cm:mlDocument or a cm:mlContainer, this node returns each + * locale that the node hasn't a translation yet. + * + * @param localizedNodeRef the cm:mlDocument or the cm:mlContainer + * @param addThisNodeLocale if true, add the locale of the given cm:mlDocument in the list. + * @return + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"localizedNodeRef", "addThisNodeLocale"}) + List getMissingTranslations(NodeRef localizedNodeRef, boolean addThisNodeLocale); + + /** + * Given any node, this returns the pivot translation. The pivot translation is the translation + * that its locale is referenced by the locale of the MLContainer. The translation can't be an + * empty translation. + * + * @param nodeRef the node to test + * @return the pivot translation + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"}) + NodeRef getPivotTranslation(NodeRef nodeRef); + + /** + * Make a empty translation out of an existing document. The necessary translation structures will be created + * as necessary. + * + * @param translationOfNodeRef An existing cm:mlDocument or cm:mlContainer + * @param name The name of the translation to create + * @return Returns the new created cm:mlEmptyTranslation + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"translationOfNodeRef", "name", "locale"}) + NodeRef addEmptyTranslation(NodeRef translationOfNodeRef, String name, Locale locale); }