Merging DEV_TEMPORARY to HEAD (RenditionService)

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19103 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Neil McErlean
2010-03-05 20:02:52 +00:00
parent ad84497735
commit bce28a5599
80 changed files with 10611 additions and 1815 deletions

View File

@@ -9,6 +9,7 @@
<import resource="classpath*:alfresco/office-addin-context.xml"/> <import resource="classpath*:alfresco/office-addin-context.xml"/>
<import resource="classpath*:alfresco/portlets-context.xml"/> <import resource="classpath*:alfresco/portlets-context.xml"/>
<import resource="classpath:alfresco/blog-context.xml"/> <import resource="classpath:alfresco/blog-context.xml"/>
<import resource="classpath:alfresco/rendition-services-context.xml"/>
<import resource="classpath:alfresco/thumbnail-service-context.xml"/> <import resource="classpath:alfresco/thumbnail-service-context.xml"/>
<import resource="classpath:alfresco/preference-service-context.xml"/> <import resource="classpath:alfresco/preference-service-context.xml"/>
<import resource="classpath:alfresco/swf-transform-context.xml"/> <import resource="classpath:alfresco/swf-transform-context.xml"/>

View File

@@ -1068,6 +1068,7 @@
<!-- Content models --> <!-- Content models -->
<value>alfresco/model/contentModel.xml</value> <value>alfresco/model/contentModel.xml</value>
<value>alfresco/model/renditionModel.xml</value>
<value>alfresco/model/bpmModel.xml</value> <value>alfresco/model/bpmModel.xml</value>
<value>alfresco/model/wcmModel.xml</value> <value>alfresco/model/wcmModel.xml</value>
<value>alfresco/model/forumModel.xml</value> <value>alfresco/model/forumModel.xml</value>

View File

@@ -356,6 +356,7 @@
<prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop> <prop key="spaces.imap_templates.childname">${spaces.imap_templates.childname}</prop>
<prop key="spaces.emailActions.childname">${spaces.emailActions.childname}</prop> <prop key="spaces.emailActions.childname">${spaces.emailActions.childname}</prop>
<prop key="spaces.searchAction.childname">${spaces.searchAction.childname}</prop> <prop key="spaces.searchAction.childname">${spaces.searchAction.childname}</prop>
<prop key="spaces.rendition.rendering_actions.childname">${spaces.rendition.rendering_actions.childname}</prop>
<prop key="spaces.wcm_deployed.childname">${spaces.wcm_deployed.childname}</prop> <prop key="spaces.wcm_deployed.childname">${spaces.wcm_deployed.childname}</prop>
</props> </props>
</property> </property>
@@ -565,6 +566,12 @@
<prop key="location">alfresco/bootstrap/transferSpaces.xml</prop> <prop key="location">alfresco/bootstrap/transferSpaces.xml</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop> <prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props> </props>
<props>
<prop key="path">/${spaces.company_home.childname}/${spaces.dictionary.childname}</prop>
<prop key="location">alfresco/bootstrap/systemRenditionDefinitions.acp</prop>
<prop key="messages">alfresco/messages/bootstrap-spaces</prop>
</props>
</list> </list>
</property> </property>
</bean> </bean>

View File

@@ -30,6 +30,9 @@ spaces.templates.email.description=Email templates
spaces.templates.rss.name=RSS Templates spaces.templates.rss.name=RSS Templates
spaces.templates.rss.description=RSS templates spaces.templates.rss.description=RSS templates
spaces.rendition.rendering_actions.name=Rendering Actions Space
spaces.rendition.rendering_actions.description=A space used by the system to persist rendering actions.
spaces.savedsearches.name=Saved Searches spaces.savedsearches.name=Saved Searches
spaces.savedsearches.description=Saved Searches spaces.savedsearches.description=Saved Searches

View File

@@ -96,6 +96,10 @@ patch.rssTemplatesFolder.description=Ensures the existence of the 'RSS Templates
patch.rssTemplatesFolder.result.exists=The RSS Templates folder already exists: {0}. Re-applying guest permissions. patch.rssTemplatesFolder.result.exists=The RSS Templates folder already exists: {0}. Re-applying guest permissions.
patch.rssTemplatesFolder.result.created=The RSS Templates folder was successfully created: {0} patch.rssTemplatesFolder.result.created=The RSS Templates folder was successfully created: {0}
patch.rendition.rendering_actions.exists=The Rendering Actions folder already exists: {0}.
patch.rendition.rendering_actions.created=The Rendering Actions folder was successfully created: {0}
patch.rendition.rendering_actions.description=Creates the Rendering Actions folder.
patch.uifacetsAspectRemovalPatch.description=Removes the incorrectly applied uifacets aspect from presentation template files. patch.uifacetsAspectRemovalPatch.description=Removes the incorrectly applied uifacets aspect from presentation template files.
patch.uifacetsAspectRemovalPatch.updated=Successfully removed the uifacets aspect from {0} presentation template files. patch.uifacetsAspectRemovalPatch.updated=Successfully removed the uifacets aspect from {0} presentation template files.
@@ -295,3 +299,6 @@ patch.transferDefinitions.result=Transfer definitions folder added to data dicti
patch.redeployNominatedInvitationProcessWithPropsForShare.description=Redeploy nominated invitation workflow patch.redeployNominatedInvitationProcessWithPropsForShare.description=Redeploy nominated invitation workflow
patch.redeployNominatedInvitationProcessWithPropsForShare.result=Nominated invitation workflow redeployed patch.redeployNominatedInvitationProcessWithPropsForShare.result=Nominated invitation workflow redeployed
patch.thumbnailsAssocQName.description=Update the 'cm:thumbnails' association QName to 'rn:rendition'.
patch.QNamePatch.result=Successfully updated the ''{0}'' QName to ''{1}''.

View File

@@ -0,0 +1,4 @@
# Rendering Actions
reformat.title=Reformat content
reformat.description=Renders a piece of content in another format (MIME type).

View File

@@ -417,27 +417,7 @@
<aspect>sys:localized</aspect> <aspect>sys:localized</aspect>
</mandatory-aspects> </mandatory-aspects>
</type> </type>
<!-- Thumbnail content type -->
<type name="cm:thumbnail">
<title>Thumbnail</title>
<parent>cm:content</parent>
<archive>true</archive>
<includedInSuperTypeQuery>false</includedInSuperTypeQuery>
<properties>
<property name="cm:thumbnailName">
<title>Thumbnail Name</title>
<type>d:text</type>
<mandatory>false</mandatory>
</property>
<property name="cm:contentPropertyName">
<title>Thumbnailed Content Property Name</title>
<type>d:qname</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>
</types> </types>
@@ -1008,32 +988,6 @@
</mandatory-aspects> </mandatory-aspects>
</aspect> </aspect>
<!-- Thumbnailed aspect relates a content node to it's thumbnails -->
<aspect name="cm:thumbnailed">
<title>Thumbnailed</title>
<properties>
<property name="cm:automaticUpdate">
<title>Automatic Update</title>
<type>d:boolean</type>
<mandatory>true</mandatory>
<default>true</default>
</property>
</properties>
<associations>
<child-association name="cm:thumbnails">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:thumbnail</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</child-association>
</associations>
</aspect>
<aspect name="cm:storeSelector"> <aspect name="cm:storeSelector">
<title>ContentStore Selector</title> <title>ContentStore Selector</title>
<properties> <properties>
@@ -1093,7 +1047,6 @@
</property> </property>
</properties> </properties>
</aspect> </aspect>
</aspects> </aspects>
</model> </model>

View File

@@ -0,0 +1,111 @@
<model name="rn:renditionmodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<!-- The Alfresco Rendition Service Model (since 3.3) -->
<description>Alfresco Rendition Model</description>
<author>Alfresco</author>
<published>2010-01-14</published>
<version>1.0</version>
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports>
<namespaces>
<namespace uri="http://www.alfresco.org/model/rendition/1.0" prefix="rn"/>
</namespaces>
<types>
<!-- Thumbnail content type -->
<type name="cm:thumbnail">
<title>Thumbnail</title>
<parent>cm:content</parent>
<archive>true</archive>
<includedInSuperTypeQuery>false</includedInSuperTypeQuery>
<properties>
<property name="cm:thumbnailName">
<title>Thumbnail Name</title>
<type>d:text</type>
<mandatory>false</mandatory>
</property>
<property name="cm:contentPropertyName">
<title>Thumbnailed Content Property Name</title>
<type>d:qname</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>
</types>
<aspects>
<!-- This aspect marks a node as a rendition. -->
<aspect name="rn:rendition">
<title>Rendition</title>
</aspect>
<!-- A hidden rendition is one which is located directly under its source node.
-->
<aspect name="rn:hiddenRendition">
<title>Hidden Rendition</title>
<parent>rn:rendition</parent>
</aspect>
<!-- A visible rendition is one which is located somewhere other than under its source node.
-->
<aspect name="rn:visibleRendition">
<title>Visible Rendition</title>
<parent>rn:rendition</parent>
</aspect>
<!-- This aspect is applied to nodes which have been "rendered" using the rendition service.
-->
<aspect name="rn:renditioned">
<title>Renditioned</title>
<associations>
<!-- This association links the source content node to the rendition -->
<child-association name="rn:rendition">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</child-association>
</associations>
</aspect>
<!-- Thumbnailed aspect relates a content node to it's thumbnails -->
<aspect name="cm:thumbnailed">
<title>Thumbnailed</title>
<parent>rn:renditioned</parent>
<properties>
<property name="cm:automaticUpdate">
<title>Automatic Update</title>
<type>d:boolean</type>
<mandatory>true</mandatory>
<default>true</default>
</property>
</properties>
<!--
<associations>
<child-association name="cm:thumbnails">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:thumbnail</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</child-association>
</associations>
-->
</aspect>

View File

@@ -1371,7 +1371,7 @@
<property name="importerBootstrap"> <property name="importerBootstrap">
<ref bean="spacesBootstrap" /> <ref bean="spacesBootstrap" />
</property> </property>
<property name="checkPath"> <property name="checkPath">
<value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/${spaces.templates.email.invite.childname}</value> <value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.templates.email.childname}/${spaces.templates.email.invite.childname}</value>
</property> </property>
<property name="bootstrapView"> <property name="bootstrapView">
@@ -2008,7 +2008,7 @@
</list> </list>
</property> </property>
</bean> </bean>
<bean id="patch.db-V3.3-Remove-VersionCount" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch"> <bean id="patch.db-V3.3-Remove-VersionCount" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
<property name="id"><value>patch.db-V3.3-Remove-VersionCount</value></property> <property name="id"><value>patch.db-V3.3-Remove-VersionCount</value></property>
<property name="description"><value>patch.schemaUpgradeScript.description</value></property> <property name="description"><value>patch.schemaUpgradeScript.description</value></property>
@@ -2020,6 +2020,64 @@
</property> </property>
</bean> </bean>
<bean id="patch.rendition.rendering_actions" class="org.alfresco.repo.admin.patch.impl.GenericBootstrapPatch" parent="basePatch" >
<property name="id"><value>patch.rendition.rendering_actions</value></property>
<property name="description"><value>patch.rendition.rendering_actions.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>4003</value></property>
<property name="targetSchema"><value>4004</value></property>
<!-- bootstrap view -->
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
<property name="checkPath">
<value>/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.rendition.rendering_actions.childname}</value>
</property>
<property name="bootstrapView">
<props>
<prop key="path">/${spaces.company_home.childname}/${spaces.dictionary.childname}</prop>
<prop key="location">alfresco/bootstrap/systemRenditionDefinitions.acp</prop>
</props>
</property>
</bean>
<!-- This patch updates Thumbnails from Alfresco 3.2.x and earlier to their Alfresco 3.3
equivalents: renditions. It changes the QName of the old cm:thumbnails child-association
to rn:rendition -->
<bean id="patch.thumbnailsAssocQName" class="org.alfresco.repo.admin.patch.impl.QNamePatch" parent="basePatch" >
<property name="id"><value>patch.thumbnailsAssocQName</value></property>
<property name="description"><value>patch.thumbnailsAssocQName.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>4004</value></property>
<property name="targetSchema"><value>4005</value></property>
<property name="importerBootstrap">
<ref bean="spacesBootstrap" />
</property>
<property name="indexerAndSearcher">
<ref bean="indexerAndSearcherFactory" />
</property>
<property name="qnameDAO">
<ref bean="qnameDAO" />
</property>
<property name="qnameBefore">
<value>{http://www.alfresco.org/model/content/1.0}thumbnails</value>
</property>
<property name="qnameAfter">
<value>{http://www.alfresco.org/model/rendition/1.0}rendition</value>
</property>
<!-- This patch is of an association type QName and so no reindexing is necessary.
However, if we wanted to reindex the QName that we'd changed we could do it like so
<property name="reindexClass">
<value>TYPE</value>
or
<value>ASPECT</value>
</property>
-->
</bean>
<bean id="patch.transferDefinitionsFolder" class="org.alfresco.repo.admin.patch.impl.GenericBootstrapPatch" parent="basePatch" > <bean id="patch.transferDefinitionsFolder" class="org.alfresco.repo.admin.patch.impl.GenericBootstrapPatch" parent="basePatch" >
<property name="id"><value>patch.transferServiceFolder</value></property> <property name="id"><value>patch.transferServiceFolder</value></property>
<property name="description"><value>patch.transferDefinitions.description</value></property> <property name="description"><value>patch.transferDefinitions.description</value></property>
@@ -2042,4 +2100,5 @@
</props> </props>
</property> </property>
</bean> </bean>
</beans>
</beans>

View File

@@ -0,0 +1,147 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<!-- Rendition Service -->
<bean id="RenditionService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.rendition.RenditionService</value>
</property>
<property name="target">
<ref bean="renditionService" />
</property>
<property name="interceptorNames">
<list>
<idref local="RenditionService_transaction" />
<idref bean="AuditMethodInterceptor" />
<idref bean="exceptionTranslator" />
<idref local="RenditionService_security" />
</list>
</property>
</bean>
<!-- Rendition service transaction bean -->
<bean id="RenditionService_transaction"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<!-- Rendition service security bean -->
<bean id="RenditionService_security"
class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" />
<!-- Rendition Service base bean -->
<bean id="renditionService" class="org.alfresco.repo.rendition.RenditionServiceImpl" >
<property name="actionService" ref="ActionService"/>
<property name="serviceRegistry" ref="ServiceRegistry" />
<property name="dictionaryService" ref="dictionaryService" />
<property name="renditionDefinitionPersister" ref="renditionDefinitionPersister" />
</bean>
<bean id="perform-rendition" class="org.alfresco.repo.rendition.PerformRenditionActionExecuter" parent="action-executer">
<property name="publicAction">
<value>false</value>
</property>
<property name="nodeService" ref="NodeService" />
<property name="actionService" ref="ActionService"/>
<property name="renditionService" ref="RenditionService" />
<property name="renditionLocationResolver" ref="renditionLocationResolver" />
</bean>
<bean id="renditionDefinitionPersister" class="org.alfresco.repo.rendition.RenditionDefinitionPersisterImpl" >
<property name="runtimeActionService" ref="actionService" />
<property name="nodeService" ref="NodeService" />
</bean>
<bean id="renditionLocationResolver" class="org.alfresco.repo.rendition.StandardRenditionLocationResolverImpl" >
<property name="serviceRegistry" ref="ServiceRegistry" />
</bean>
<!-- i18n -->
<bean id="renderingActionResourceBundles"
class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<!-- <value>alfresco.messages.rendition-service</value> -->
<value>alfresco.messages.rendition-config</value>
</list>
</property>
</bean>
<!-- Rendering Action executor beans -->
<bean id="baseRenderingAction" abstract="true" parent="action-executer"
class="org.alfresco.repo.rendition.executer.AbstractRenderingEngine">
<property name="defaultRenditionContentProp"
value="{http://www.alfresco.org/model/content/1.0}content" />
<property name="defaultRenditionNodeType"
value="{http://www.alfresco.org/model/content/1.0}content" />
<property name="mimetypeMap" ref="mimetypeService" />
<property name="nodeService">
<ref bean="NodeService" />
</property>
<property name="contentService">
<ref bean="ContentService" />
</property>
<property name="applicableTypes">
<list>
<value>{http://www.alfresco.org/model/content/1.0}content</value>
</list>
</property>
</bean>
<!-- Rendering Engines -->
<bean id="reformat"
class="org.alfresco.repo.rendition.executer.ReformatRenderingEngine"
parent="baseRenderingAction">
</bean>
<bean id="imageRenderingEngine"
class="org.alfresco.repo.rendition.executer.ImageRenderingEngine"
parent="baseRenderingAction">
</bean>
<bean id="templatingRenderingEngine"
class="org.alfresco.repo.rendition.executer.TemplatingRenderingEngine"
parent="baseRenderingAction">
<property name="templateService">
<ref bean="templateService" />
</property>
<property name="repositoryHelper">
<ref bean="repositoryHelper" />
</property>
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
</bean>
<bean id="compositeRenderingEngine"
class="org.alfresco.repo.rendition.executer.CompositeRenderingEngine"
parent="baseRenderingAction">
<property name="actionService">
<ref bean="actionService" />
</property>
</bean>
<!-- Behaviours and policies for Renditions -->
<bean id="renditionedAspect" class="org.alfresco.repo.rendition.RenditionedAspect" init-method="init">
<property name="nodeService" ref="NodeService"/>
<property name="dictionaryService" ref="DictionaryService"/>
<property name="policyComponent" ref="policyComponent"/>
<property name="renditionService" ref="RenditionService"/>
</bean>
</beans>

View File

@@ -315,6 +315,7 @@ spaces.content_forms.childname=app:forms
spaces.user_homes.childname=app:user_homes spaces.user_homes.childname=app:user_homes
spaces.sites.childname=st:sites spaces.sites.childname=st:sites
spaces.templates.email.invite.childname=cm:invite spaces.templates.email.invite.childname=cm:invite
spaces.rendition.rendering_actions.childname=app:rendering_actions
spaces.wcm_deployed.childname=cm:wcm_deployed spaces.wcm_deployed.childname=cm:wcm_deployed
spaces.transfers.childname=app:transfers spaces.transfers.childname=app:transfers
spaces.transfer_groups.childname=app:transfer_groups spaces.transfer_groups.childname=app:transfer_groups
@@ -398,10 +399,8 @@ deployment.rmi.service.port=50507
mbean.server.locateExistingServerIfPossible=true mbean.server.locateExistingServerIfPossible=true
# External executable locations # External executable locations
#ooo.exe=/Applications/OpenOffice.org.app/Contents/MacOS/soffice
ooo.exe=soffice ooo.exe=soffice
ooo.user=${dir.root}/oouser ooo.user=${dir.root}/oouser
img.root=./ImageMagick img.root=./ImageMagick
img.dyn=${img.root}/lib img.dyn=${img.root}/lib
img.exe=${img.root}/bin/convert img.exe=${img.root}/bin/convert

View File

@@ -36,11 +36,12 @@
<!-- Thumbnail service security bean --> <!-- Thumbnail service security bean -->
<bean id="ThumbnailService_security" class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" /> <bean id="ThumbnailService_security" class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" />
<!-- Thumbnail service implemenation bean --> <!-- Thumbnail service implementation bean -->
<bean id="thumbnailService" class="org.alfresco.repo.thumbnail.ThumbnailServiceImpl"> <bean id="thumbnailService" class="org.alfresco.repo.thumbnail.ThumbnailServiceImpl">
<property name="nodeService" ref="nodeService"/> <property name="nodeService" ref="nodeService"/>
<property name="contentService" ref="contentService"/> <property name="contentService" ref="contentService"/>
<property name="mimetypeMap" ref="mimetypeService"/> <property name="mimetypeMap" ref="mimetypeService"/>
<property name="renditionService" ref="renditionService" />
<property name="behaviourFilter" ref="policyBehaviourFilter" /> <property name="behaviourFilter" ref="policyBehaviourFilter" />
<property name="thumbnailRegistry" ref="thumbnailRegistry" /> <property name="thumbnailRegistry" ref="thumbnailRegistry" />
</bean> </bean>
@@ -48,96 +49,15 @@
<!-- Thumbnail Register --> <!-- Thumbnail Register -->
<bean id="thumbnailRegistry" class="org.alfresco.repo.thumbnail.ThumbnailRegistry"> <bean id="thumbnailRegistry" class="org.alfresco.repo.thumbnail.ThumbnailRegistry">
<property name="contentService" ref="ContentService"/> <property name="contentService" ref="ContentService"/>
<property name="thumbnailDefinitions"> <property name="renditionService" ref="renditionService" />
<list> <property name="thumbnails">
<list>
<!-- Small image thumbnail options --> <value>medium</value>
<bean class="org.alfresco.repo.thumbnail.ThumbnailDefinition"> <value>doclib</value>
<property name="name" value="medium" /> <value>webpreview</value>
<property name="mimetype" value="image/jpeg"/> <value>imgpreview</value>
<property name="transformationOptions"> <value>avatar</value>
<bean class="org.alfresco.repo.content.transform.magick.ImageTransformationOptions"> </list>
<property name="resizeOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageResizeOptions">
<property name="width" value="100"/>
<property name="height" value="100"/>
<property name="maintainAspectRatio" value="true"/>
<property name="resizeToThumbnail" value="true" />
</bean>
</property>
</bean>
</property>
<property name="placeHolderResourcePath" value="alfresco/thumbnail/thumbnail_placeholder_medium.jpg" />
</bean>
<!-- Slingshot Document Library image thumbnail options -->
<bean class="org.alfresco.repo.thumbnail.ThumbnailDefinition">
<property name="name" value="doclib" />
<property name="mimetype" value="image/png"/>
<property name="transformationOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageTransformationOptions">
<property name="resizeOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageResizeOptions">
<property name="width" value="100"/>
<property name="height" value="100"/>
<property name="maintainAspectRatio" value="true"/>
<property name="resizeToThumbnail" value="true" />
</bean>
</property>
</bean>
</property>
<property name="placeHolderResourcePath" value="alfresco/thumbnail/thumbnail_placeholder_doclib.png" />
</bean>
<!-- Web Preview thumbnail options -->
<bean class="org.alfresco.repo.thumbnail.ThumbnailDefinition">
<property name="name" value="webpreview" />
<property name="mimetype" value="application/x-shockwave-flash"/>
<property name="transformationOptions">
<bean class="org.alfresco.repo.content.transform.swf.SWFTransformationOptions">
<property name="flashVersion" value="9"/>
</bean>
</property>
</bean>
<!-- Image preview thumbnail options -->
<bean class="org.alfresco.repo.thumbnail.ThumbnailDefinition">
<property name="name" value="imgpreview" />
<property name="mimetype" value="image/png"/>
<property name="transformationOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageTransformationOptions">
<property name="resizeOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageResizeOptions">
<property name="width" value="480"/>
<property name="height" value="480"/>
<property name="maintainAspectRatio" value="true"/>
<property name="resizeToThumbnail" value="true" />
</bean>
</property>
</bean>
</property>
<property name="placeHolderResourcePath" value="alfresco/thumbnail/thumbnail_placeholder_imgpreview.png" />
</bean>
<!-- User avatar image thumbnail options -->
<bean class="org.alfresco.repo.thumbnail.ThumbnailDefinition">
<property name="name" value="avatar" />
<property name="mimetype" value="image/png"/>
<property name="transformationOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageTransformationOptions">
<property name="resizeOptions">
<bean class="org.alfresco.repo.content.transform.magick.ImageResizeOptions">
<property name="width" value="64"/>
<property name="height" value="64"/>
<property name="maintainAspectRatio" value="true"/>
<property name="resizeToThumbnail" value="true" />
</bean>
</property>
</bean>
</property>
<property name="placeHolderResourcePath" value="alfresco/thumbnail/thumbnail_placeholder_avatar.png" />
</bean>
</list>
</property> </property>
</bean> </bean>
@@ -156,26 +76,20 @@
<!-- Update Thumbnail Action --> <!-- Update Thumbnail Action -->
<bean id="update-thumbnail" class="org.alfresco.repo.thumbnail.UpdateThumbnailActionExecuter" parent="action-executer"> <bean id="update-thumbnail" class="org.alfresco.repo.thumbnail.UpdateThumbnailActionExecuter" parent="action-executer">
<property name="publicAction"> <property name="publicAction">
<value>false</value> <value>false</value>
</property> </property>
<property name="nodeService"> <property name="nodeService">
<ref bean="NodeService" /> <ref bean="NodeService" />
</property> </property>
<property name="renditionService">
<ref bean="RenditionService" />
</property>
<property name="thumbnailService"> <property name="thumbnailService">
<ref bean="ThumbnailService" /> <ref bean="ThumbnailService" />
</property> </property>
</bean> </bean>
<!-- Thumbnailed aspect -->
<bean id="thumbnailedAspect" class="org.alfresco.repo.thumbnail.ThumbnailedAspect" init-method="init">
<property name="nodeService" ref="NodeService"/>
<property name="dictionaryService" ref="DictionaryService"/>
<property name="policyComponent" ref="policyComponent"/>
<property name="actionService" ref="ActionService"/>
<property name="thumbnailService" ref="ThumbnailService"/>
</bean>
<!-- Thumbnail service script API --> <!-- Thumbnail service script API -->
<bean id="thumbnailServiceScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.thumbnail.script.ScriptThumbnailService"> <bean id="thumbnailServiceScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.thumbnail.script.ScriptThumbnailService">
<property name="extensionName"> <property name="extensionName">

View File

@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number # Schema number
version.schema=4003 version.schema=4005

View File

@@ -264,7 +264,7 @@ public interface ContentModel
static final QName ASSOC_MULTILINGUAL_CHILD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlChild"); 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_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"); static final QName ASPECT_MULTILINGUAL_EMPTY_TRANSLATION = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mlEmptyTranslation");
// Thumbnail Type // Thumbnail Type
static final QName TYPE_THUMBNAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnail"); static final QName TYPE_THUMBNAIL = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnail");
static final QName PROP_THUMBNAIL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailName"); static final QName PROP_THUMBNAIL_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailName");

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2005-2010 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 received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.model;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Rendition Model Constants
*/
public interface RenditionModel
{
static final QName ASPECT_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "rendition");
static final QName ASPECT_HIDDEN_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "hiddenRendition");
static final QName ASPECT_VISIBLE_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "visibleRendition");
static final QName ASPECT_RENDITIONED = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "renditioned");
static final QName ASSOC_RENDITION = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "rendition");
}

View File

@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.alfresco.repo.action; package org.alfresco.repo.action;
import java.io.Serializable; import java.io.Serializable;
@@ -34,72 +35,71 @@ import org.alfresco.service.cmr.repository.NodeRef;
* *
* @author Roy Wetherall * @author Roy Wetherall
*/ */
public class ActionImpl extends ParameterizedItemImpl public class ActionImpl extends ParameterizedItemImpl implements Serializable, Action
implements Serializable, Action
{ {
/** /**
* Serial version UID * Serial version UID
*/ */
private static final long serialVersionUID = 3258135760426186548L; private static final long serialVersionUID = 3258135760426186548L;
/** The node reference for the action */ /** The node reference for the action */
private NodeRef nodeRef; private NodeRef nodeRef;
/** /**
* The title * The title
*/ */
private String title; private String title;
/** /**
* The description * The description
*/ */
private String description; private String description;
/** /**
* Inidcates whether the action should be executed asynchronously or not * Inidcates whether the action should be executed asynchronously or not
*/ */
private boolean executeAsynchronously = false; private boolean executeAsynchronously = false;
/**
* The compensating action
*/
private Action compensatingAction;
/** /**
* The created date * The compensating action
*/
private Action compensatingAction;
/**
* The created date
*/ */
private Date createdDate; private Date createdDate;
/** /**
* The creator * The creator
*/ */
private String creator; private String creator;
/** /**
* The modified date * The modified date
*/ */
private Date modifiedDate; private Date modifiedDate;
/** /**
* The modifier * The modifier
*/ */
private String modifier; private String modifier;
/** /**
* Rule action definition name * Rule action definition name
*/ */
private String actionDefinitionName; private String actionDefinitionName;
/** /**
* The run as user name * The run as user name
*/ */
private String runAsUserName; private String runAsUserName;
/** /**
* The chain of actions that have lead to this action * The chain of actions that have lead to this action
*/ */
private Set<String> actionChain; private Set<String> actionChain;
/** /**
* Action conditions * Action conditions
*/ */
@@ -108,42 +108,62 @@ public class ActionImpl extends ParameterizedItemImpl
/** /**
* Constructor * Constructor
* *
* @param nodeRef the action node reference (null if not saved) * @param nodeRef the action node reference (null if not saved)
* @param id the action id * @param id the action id
* @param actionDefinitionName the name of the action definition * @param actionDefinitionName the name of the action definition
*/ */
public ActionImpl(NodeRef nodeRef, String id, String actionDefinitionName) public ActionImpl(NodeRef nodeRef, String id, String actionDefinitionName)
{ {
this(nodeRef, id, actionDefinitionName, null); this(nodeRef, id, actionDefinitionName, null);
} }
/** /**
* Constructor * Constructor
* *
* @param nodeRef the action node reference (null if not saved) * @param nodeRef the action node reference (null if not saved)
* @param id the action id * @param id the action id
* @param actionDefinitionName the action definition name * @param actionDefinitionName the action definition name
* @param parameterValues the parameter values * @param parameterValues the parameter values
*/ */
public ActionImpl( public ActionImpl(NodeRef nodeRef, String id, String actionDefinitionName, Map<String, Serializable> parameterValues)
NodeRef nodeRef,
String id,
String actionDefinitionName,
Map<String, Serializable> parameterValues)
{ {
super(id, parameterValues); super(id, parameterValues);
this.nodeRef = nodeRef; this.nodeRef = nodeRef;
this.actionDefinitionName = actionDefinitionName; this.actionDefinitionName = actionDefinitionName;
} }
public ActionImpl(Action action, String actionDefinitionName)
{
super(action.getId(), action.getParameterValues());
this.actionDefinitionName = actionDefinitionName;
this.actionConditions = action.getActionConditions();
this.compensatingAction = action.getCompensatingAction();
this.createdDate = action.getCreatedDate();
this.creator = action.getCreator();
this.description = action.getDescription();
this.executeAsynchronously = action.getExecuteAsychronously();
this.modifiedDate = action.getModifiedDate();
this.modifier = action.getModifier();
this.nodeRef = action.getNodeRef();
this.title = action.getTitle();
if (action instanceof ActionImpl)
{
ActionImpl actionImpl = (ActionImpl) action;
this.runAsUserName = actionImpl.getRunAsUser();
this.actionChain = actionImpl.actionChain;
}
}
public ActionImpl(Action action)
{
this(action, action.getActionDefinitionName());
}
@Override @Override
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Action") sb.append("Action").append("[ id=").append(getId()).append(", node=").append(nodeRef).append(" ]");
.append("[ id=").append(getId())
.append(", node=").append(nodeRef)
.append(" ]");
return sb.toString(); return sb.toString();
} }
@@ -151,232 +171,234 @@ public class ActionImpl extends ParameterizedItemImpl
* @see org.alfresco.service.cmr.action.Action#getTitle() * @see org.alfresco.service.cmr.action.Action#getTitle()
*/ */
public String getTitle() public String getTitle()
{ {
return this.title; return this.title;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#setTitle(java.lang.String) * @see org.alfresco.service.cmr.action.Action#setTitle(java.lang.String)
*/ */
public void setTitle(String title) public void setTitle(String title)
{ {
this.title = title; this.title = title;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#getDescription() * @see org.alfresco.service.cmr.action.Action#getDescription()
*/ */
public String getDescription() public String getDescription()
{ {
return this.description; return this.description;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#setDescription(java.lang.String) * @see org.alfresco.service.cmr.action.Action#setDescription(java.lang.String)
*/ */
public void setDescription(String description) public void setDescription(String description)
{ {
this.description = description; this.description = description;
} }
/**
* @see org.alfresco.service.cmr.action.Action#getExecuteAsychronously()
*/
public boolean getExecuteAsychronously()
{
return this.executeAsynchronously ;
}
/** /**
* @see org.alfresco.service.cmr.action.Action#setExecuteAsynchronously(boolean) * @see org.alfresco.service.cmr.action.Action#getExecuteAsychronously()
*/ */
public void setExecuteAsynchronously(boolean executeAsynchronously) public boolean getExecuteAsychronously()
{ {
this.executeAsynchronously = executeAsynchronously; return this.executeAsynchronously;
} }
/**
* @see org.alfresco.service.cmr.action.Action#getCompensatingAction()
*/
public Action getCompensatingAction()
{
return this.compensatingAction;
}
/**
* @see org.alfresco.service.cmr.action.Action#setCompensatingAction(org.alfresco.service.cmr.action.Action)
*/
public void setCompensatingAction(Action action)
{
this.compensatingAction = action;
}
/** /**
* @see org.alfresco.service.cmr.action.Action#getCreatedDate() * @see org.alfresco.service.cmr.action.Action#setExecuteAsynchronously(boolean)
*/ */
public Date getCreatedDate() public void setExecuteAsynchronously(boolean executeAsynchronously)
{ {
return this.createdDate; this.executeAsynchronously = executeAsynchronously;
} }
/**
* Set the created date
*
* @param createdDate the created date
*/
public void setCreatedDate(Date createdDate)
{
this.createdDate = createdDate;
}
/** /**
* @see org.alfresco.service.cmr.action.Action#getCreator() * @see org.alfresco.service.cmr.action.Action#getCompensatingAction()
*/ */
public String getCreator() public Action getCompensatingAction()
{ {
return this.creator; return this.compensatingAction;
} }
/**
* Set the creator
*
* @param creator the creator
*/
public void setCreator(String creator)
{
this.creator = creator;
}
/** /**
* @see org.alfresco.service.cmr.action.Action#getModifiedDate() * @see org.alfresco.service.cmr.action.Action#setCompensatingAction(org.alfresco.service.cmr.action.Action)
*/ */
public Date getModifiedDate() public void setCompensatingAction(Action action)
{ {
return this.modifiedDate; this.compensatingAction = action;
} }
/**
* Set the modified date
*
* @param modifiedDate the modified date
*/
public void setModifiedDate(Date modifiedDate)
{
this.modifiedDate = modifiedDate;
}
/** /**
* @see org.alfresco.service.cmr.action.Action#getModifier() * @see org.alfresco.service.cmr.action.Action#getCreatedDate()
*/ */
public String getModifier() public Date getCreatedDate()
{ {
return this.modifier; return this.createdDate;
} }
/** /**
* Set the modifier * Set the created date
* *
* @param modifier the modifier * @param createdDate the created date
*/ */
public void setModifier(String modifier) public void setCreatedDate(Date createdDate)
{ {
this.modifier = modifier; this.createdDate = createdDate;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#getActionDefinitionName() * @see org.alfresco.service.cmr.action.Action#getCreator()
*/ */
public String getCreator()
{
return this.creator;
}
/**
* Set the creator
*
* @param creator the creator
*/
public void setCreator(String creator)
{
this.creator = creator;
}
/**
* @see org.alfresco.service.cmr.action.Action#getModifiedDate()
*/
public Date getModifiedDate()
{
return this.modifiedDate;
}
/**
* Set the modified date
*
* @param modifiedDate the modified date
*/
public void setModifiedDate(Date modifiedDate)
{
this.modifiedDate = modifiedDate;
}
/**
* @see org.alfresco.service.cmr.action.Action#getModifier()
*/
public String getModifier()
{
return this.modifier;
}
/**
* Set the modifier
*
* @param modifier the modifier
*/
public void setModifier(String modifier)
{
this.modifier = modifier;
}
/**
* @see org.alfresco.service.cmr.action.Action#getActionDefinitionName()
*/
public String getActionDefinitionName() public String getActionDefinitionName()
{ {
return this.actionDefinitionName; return this.actionDefinitionName;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#hasActionConditions() * @see org.alfresco.service.cmr.action.Action#hasActionConditions()
*/ */
public boolean hasActionConditions() public boolean hasActionConditions()
{ {
return (this.actionConditions.isEmpty() == false); return (this.actionConditions.isEmpty() == false);
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#indexOfActionCondition(org.alfresco.service.cmr.action.ActionCondition) * @see org.alfresco.service.cmr.action.Action#indexOfActionCondition(org.alfresco.service.cmr.action.ActionCondition)
*/ */
public int indexOfActionCondition(ActionCondition actionCondition) public int indexOfActionCondition(ActionCondition actionCondition)
{ {
return this.actionConditions.indexOf(actionCondition); return this.actionConditions.indexOf(actionCondition);
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#getActionConditions() * @see org.alfresco.service.cmr.action.Action#getActionConditions()
*/ */
public List<ActionCondition> getActionConditions() public List<ActionCondition> getActionConditions()
{ {
return this.actionConditions; return this.actionConditions;
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#getActionCondition(int) * @see org.alfresco.service.cmr.action.Action#getActionCondition(int)
*/ */
public ActionCondition getActionCondition(int index) public ActionCondition getActionCondition(int index)
{ {
return this.actionConditions.get(index); return this.actionConditions.get(index);
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#addActionCondition(org.alfresco.service.cmr.action.ActionCondition) * @see org.alfresco.service.cmr.action.Action#addActionCondition(org.alfresco.service.cmr.action.ActionCondition)
*/ */
public void addActionCondition(ActionCondition actionCondition) public void addActionCondition(ActionCondition actionCondition)
{ {
this.actionConditions.add(actionCondition); this.actionConditions.add(actionCondition);
} }
/** /**
* @see org.alfresco.service.cmr.action.Action#addActionCondition(int, org.alfresco.service.cmr.action.ActionCondition) * @see org.alfresco.service.cmr.action.Action#addActionCondition(int,
*/ * org.alfresco.service.cmr.action.ActionCondition)
public void addActionCondition(int index, ActionCondition actionCondition) */
{ public void addActionCondition(int index, ActionCondition actionCondition)
this.actionConditions.add(index, actionCondition); {
} this.actionConditions.add(index, actionCondition);
}
/** /**
* @see org.alfresco.service.cmr.action.Action#setActionCondition(int, org.alfresco.service.cmr.action.ActionCondition) * @see org.alfresco.service.cmr.action.Action#setActionCondition(int,
*/ * org.alfresco.service.cmr.action.ActionCondition)
public void setActionCondition(int index, ActionCondition actionCondition) */
{ public void setActionCondition(int index, ActionCondition actionCondition)
this.actionConditions.set(index, actionCondition); {
} this.actionConditions.set(index, actionCondition);
}
/** /**
* @see org.alfresco.service.cmr.action.Action#removeActionCondition(org.alfresco.service.cmr.action.ActionCondition) * @see org.alfresco.service.cmr.action.Action#removeActionCondition(org.alfresco.service.cmr.action.ActionCondition)
*/ */
public void removeActionCondition(ActionCondition actionCondition) public void removeActionCondition(ActionCondition actionCondition)
{ {
this.actionConditions.remove(actionCondition); this.actionConditions.remove(actionCondition);
} }
/**
* @see org.alfresco.service.cmr.action.Action#removeAllActionConditions()
*/
public void removeAllActionConditions()
{
this.actionConditions.clear();
}
/**
* @see org.alfresco.service.cmr.action.Action#removeAllActionConditions()
*/
public void removeAllActionConditions()
{
this.actionConditions.clear();
}
/** /**
* Set the action chain * Set the action chain
* *
* @param actionChain the list of actions that lead to this action * @param actionChain the list of actions that lead to this action
*/ */
public void setActionChain(Set<String> actionChain) public void setActionChain(Set<String> actionChain)
{ {
this.actionChain = actionChain; this.actionChain = actionChain;
} }
/** /**
* Get the action chain * Get the action chain
* *
* @return the list of actions that lead to this action * @return the list of actions that lead to this action
*/ */
public Set<String> getActionChain() public Set<String> getActionChain()
{ {
@@ -400,11 +422,11 @@ public class ActionImpl extends ParameterizedItemImpl
{ {
return this.nodeRef; return this.nodeRef;
} }
/** /**
* Set the node reference * Set the node reference
* *
* @param nodeRef the node reference * @param nodeRef the node reference
*/ */
public void setNodeRef(NodeRef nodeRef) public void setNodeRef(NodeRef nodeRef)
{ {

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2005-2010 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.action;
import java.util.LinkedList;
import java.util.List;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionList;
/**
* @author Nick Smith
*/
public class ActionListImpl<A extends Action> implements ActionList<A>
{
/**
* Serial Version UID
*/
private static final long serialVersionUID = -1578631012627795870L;
/**
* The action list
*/
private final List<A> actions;
public ActionListImpl()
{
this.actions = new LinkedList<A>();
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#hasActions()
*/
public boolean hasActions()
{
return (this.actions.isEmpty() == false);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#addAction(org.alfresco.service.cmr.action.Action)
*/
public void addAction(A action)
{
this.actions.add(action);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#addAction(int,
* org.alfresco.service.cmr.action.Action)
*/
public void addAction(int index, A action)
{
this.actions.add(index, action);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#setAction(int,
* org.alfresco.service.cmr.action.Action)
*/
public void setAction(int index, A action)
{
this.actions.set(index, action);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#indexOfAction(org.alfresco.service.cmr.action.Action)
*/
public int indexOfAction(A action)
{
return this.actions.indexOf(action);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#getActions()
*/
public List<A> getActions()
{
return this.actions;
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#getAction(int)
*/
public A getAction(int index)
{
return this.actions.get(index);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#removeAction(org.alfresco.service.cmr.action.Action)
*/
public void removeAction(A action)
{
this.actions.remove(action);
}
/**
* @see org.alfresco.service.cmr.action.CompositeAction#removeAllActions()
*/
public void removeAllActions()
{
this.actions.clear();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,13 +16,14 @@
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.alfresco.repo.action; package org.alfresco.repo.action;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.alfresco.repo.action.executer.CompositeActionExecuter; import org.alfresco.repo.action.executer.CompositeActionExecuter;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionList;
import org.alfresco.service.cmr.action.CompositeAction; import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -33,96 +34,106 @@ import org.alfresco.service.cmr.repository.NodeRef;
*/ */
public class CompositeActionImpl extends ActionImpl implements CompositeAction public class CompositeActionImpl extends ActionImpl implements CompositeAction
{ {
/** /**
* Serial version UID * Serial version UID
*/ */
private static final long serialVersionUID = -5348203599304776812L; private static final long serialVersionUID = -5348203599304776812L;
/**
* The action list
*/
private List<Action> actions = new ArrayList<Action>();
/**
* Constructor
*
* @param id the action id
*/
public CompositeActionImpl(NodeRef nodeRef, String id)
{
super(nodeRef, id, CompositeActionExecuter.NAME);
}
/** private final ActionList<Action> actions = new ActionListImpl<Action>();
* @see org.alfresco.service.cmr.action.CompositeAction#hasActions()
*/
public boolean hasActions()
{
return (this.actions.isEmpty() == false);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#addAction(org.alfresco.service.cmr.action.Action) * Constructor
*/ *
public void addAction(Action action) * @param id the action id
{ */
this.actions.add(action); public CompositeActionImpl(NodeRef nodeRef, String id)
} {
super(nodeRef, id, CompositeActionExecuter.NAME);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#addAction(int, org.alfresco.service.cmr.action.Action) * @param action
*/ * @see org.alfresco.service.cmr.action.ActionList#addAction(org.alfresco.service.cmr.action.Action)
public void addAction(int index, Action action) */
{ public void addAction(Action action)
this.actions.add(index, action); {
} this.actions.addAction(action);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#setAction(int, org.alfresco.service.cmr.action.Action) * @param index
*/ * @param action
public void setAction(int index, Action action) * @see org.alfresco.service.cmr.action.ActionList#addAction(int,
{ * org.alfresco.service.cmr.action.Action)
this.actions.set(index, action); */
} public void addAction(int index, Action action)
{
this.actions.addAction(index, action);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#indexOfAction(org.alfresco.service.cmr.action.Action) * @param index
*/ * @return
public int indexOfAction(Action action) * @see org.alfresco.service.cmr.action.ActionList#getAction(int)
{ */
return this.actions.indexOf(action); public Action getAction(int index)
} {
return this.actions.getAction(index);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#getActions() * @return
*/ * @see org.alfresco.service.cmr.action.ActionList#getActions()
public List<Action> getActions() */
{ public List<Action> getActions()
return this.actions; {
} return this.actions.getActions();
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#getAction(int) * @return
*/ * @see org.alfresco.service.cmr.action.ActionList#hasActions()
public Action getAction(int index) */
{ public boolean hasActions()
return this.actions.get(index); {
} return this.actions.hasActions();
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#removeAction(org.alfresco.service.cmr.action.Action) * @param action
*/ * @return
public void removeAction(Action action) * @see org.alfresco.service.cmr.action.ActionList#indexOfAction(org.alfresco.service.cmr.action.Action)
{ */
this.actions.remove(action); public int indexOfAction(Action action)
} {
return this.actions.indexOfAction(action);
}
/** /**
* @see org.alfresco.service.cmr.action.CompositeAction#removeAllActions() * @param action
*/ * @see org.alfresco.service.cmr.action.ActionList#removeAction(org.alfresco.service.cmr.action.Action)
public void removeAllActions() */
{ public void removeAction(Action action)
this.actions.clear(); {
} this.actions.removeAction(action);
}
/**
* @see org.alfresco.service.cmr.action.ActionList#removeAllActions()
*/
public void removeAllActions()
{
this.actions.removeAllActions();
}
/**
* @param index
* @param action
* @see org.alfresco.service.cmr.action.ActionList#setAction(int,
* org.alfresco.service.cmr.action.Action)
*/
public void setAction(int index, Action action)
{
this.actions.setAction(index, action);
}
} }

View File

@@ -29,6 +29,7 @@ import org.alfresco.service.cmr.action.ParameterizedItem;
* *
* @author Roy Wetherall * @author Roy Wetherall
*/ */
@SuppressWarnings("serial")
public abstract class ParameterizedItemImpl implements ParameterizedItem, Serializable public abstract class ParameterizedItemImpl implements ParameterizedItem, Serializable
{ {
/** /**

View File

@@ -98,7 +98,7 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra
{ {
if (this.actionDefinition == null) if (this.actionDefinition == null)
{ {
this.actionDefinition = new ActionDefinitionImpl(this.name); this.actionDefinition = createActionDefinition(this.name);
((ActionDefinitionImpl)this.actionDefinition).setTitleKey(getTitleKey()); ((ActionDefinitionImpl)this.actionDefinition).setTitleKey(getTitleKey());
((ActionDefinitionImpl)this.actionDefinition).setDescriptionKey(getDescriptionKey()); ((ActionDefinitionImpl)this.actionDefinition).setDescriptionKey(getDescriptionKey());
((ActionDefinitionImpl)this.actionDefinition).setAdhocPropertiesAllowed(getAdhocPropertiesAllowed()); ((ActionDefinitionImpl)this.actionDefinition).setAdhocPropertiesAllowed(getAdhocPropertiesAllowed());
@@ -109,6 +109,18 @@ public abstract class ActionExecuterAbstractBase extends ParameterizedItemAbstra
return this.actionDefinition; return this.actionDefinition;
} }
/**
* This method returns an instance of an ActionDefinition implementation class. By default
* this will be an {@link ActionDefinitionImpl}, but this could be overridden.
*
* @param name
* @return
*/
protected ActionDefinition createActionDefinition(String name)
{
return new ActionDefinitionImpl(name);
}
/** /**
* @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef)
*/ */

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.admin.patch.impl;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.search.Indexer;
import org.alfresco.repo.search.IndexerAndSearcher;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* A patch to update the value of a QName.
* This patch will only succeed if the target QName has not been used i.e. if there is no content
* that actually references the QName.
* <P/>
* A property 'reindexClass' can be optionally injected. If it is not injected then the QName is
* updated and no reindexing is requested by this patch.
* If it is set to either 'TYPE' or 'ASPECT' (as appropriate) then that String will be used to
* locate out-of-date references to the old QName and have them reindexed in a targetted way.
* <P/>
* Please refer to the implementation in this class for the details of how this is achieved.
*
* @author Neil McErlean
*/
public class QNamePatch extends AbstractPatch
{
private static final String MSG_SUCCESS = "patch.QNamePatch.result";
/* Injected properties */
private String qnameStringBefore;
private String qnameStringAfter;
private String reindexClass;
/* Injected services */
private ImporterBootstrap importerBootstrap;
private IndexerAndSearcher indexerAndSearcher;
private QNameDAO qnameDAO;
/**
* Sets the importerBootstrap.
* @param importerBootstrap.
*/
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
}
/**
* Sets the IndexerAndSearcher.
* @param indexerAndSearcher
*/
public void setIndexerAndSearcher(IndexerAndSearcher indexerAndSearcher)
{
this.indexerAndSearcher = indexerAndSearcher;
}
/**
* Sets the QNameDAO.
* @param qnameDAO
*/
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
/**
* Sets the QName to be patched.
* @param qnameStringBefore the long-form QName to be patched from. {namespaceURI}localName
*/
public void setQnameBefore(String qnameStringBefore)
{
this.qnameStringBefore = qnameStringBefore;
}
/**
* Sets the new QName value to be used.
* @param qnameStringAfter the long-form QName to be patched to. {namespaceURI}localName
*/
public void setQnameAfter(String qnameStringAfter)
{
this.qnameStringAfter = qnameStringAfter;
}
/**
* Sets a value for the class to reindex. This will be used in the Lucene query below and
* should be either "TYPE" or "ASPECT" or not set if reindexing is not required.
* @param reindexClass "TYPE" or "ASPECT" or not set.
*/
public void setReindexClass(String reindexClass)
{
this.reindexClass = reindexClass;
}
@Override
protected String applyInternal() throws Exception
{
// We don't need to catch the potential InvalidQNameException here as it will be caught
// in AbstractPatch and correctly handled there
QName qnameBefore = QName.createQName(this.qnameStringBefore);
QName qnameAfter = QName.createQName(this.qnameStringAfter);
if (qnameDAO.getQName(qnameBefore) != null)
{
qnameDAO.updateQName(qnameBefore, qnameAfter);
}
// Optionally perform a focussed reindexing of the removed QName.
if ("TYPE".equals(reindexClass) ||
"ASPECT".equals(reindexClass))
{
reindex(reindexClass + ":" + LuceneQueryParser.escape(qnameStringBefore), importerBootstrap.getStoreRef());
}
return I18NUtil.getMessage(MSG_SUCCESS, qnameBefore, qnameAfter);
}
private int reindex(String query, StoreRef store)
{
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery(query);
sp.addStore(store);
Indexer indexer = indexerAndSearcher.getIndexer(store);
ResultSet rs = null;
int count = 0;
try
{
rs = searchService.query(sp);
count = rs.length();
for (ResultSetRow row : rs)
{
indexer.updateNode(row.getNodeRef());
}
}
finally
{
if (rs != null)
{
rs.close();
}
}
return count;
}
}

View File

@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.alfresco.repo.content.metadata.xml; package org.alfresco.repo.content.metadata.xml;
import java.io.Serializable; import java.io.Serializable;
@@ -26,37 +27,38 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.content.selector.ContentWorkerSelector;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter; import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.repo.content.metadata.MetadataExtracter; import org.alfresco.repo.content.metadata.MetadataExtracter;
import org.alfresco.repo.content.selector.ContentWorkerSelector;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.springframework.extensions.surf.util.PropertyCheck;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.PropertyCheck;
/** /**
* A metadata extractor that selects an appropiate workder for the extraction. * A metadata extractor that selects an appropiate workder for the extraction.
* <p> * <p>
* The {@linkplain #setSelectors(List) selectors} are used to find an extracter most * The {@linkplain #setSelectors(List) selectors} are used to find an extracter
* appropriate of a given XML document. The chosen extracter is then asked to extract * most appropriate of a given XML document. The chosen extracter is then asked
* the values, passing through the {@linkplain MetadataExtracter.OverwritePolicy overwrite policy} * to extract the values, passing through the
* as {@linkplain #setOverwritePolicy(String)} on this instance. The overwrite policy of the * {@linkplain MetadataExtracter.OverwritePolicy overwrite policy} as
* embedded extracters is not relevant unless they are used separately in another context. * {@linkplain #setOverwritePolicy(String)} on this instance. The overwrite
* policy of the embedded extracters is not relevant unless they are used
* separately in another context.
* *
* @see ContentWorkerSelector * @see ContentWorkerSelector
* @see MetadataExtracter * @see MetadataExtracter
*
* @since 2.1 * @since 2.1
* @author Derek Hulley * @author Derek Hulley
*/ */
public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
{ {
public static String[] SUPPORTED_MIMETYPES = new String[] {MimetypeMap.MIMETYPE_XML}; public static String[] SUPPORTED_MIMETYPES = new String[] { MimetypeMap.MIMETYPE_XML };
private static Log logger = LogFactory.getLog(XPathMetadataExtracter.class); private static Log logger = LogFactory.getLog(XPathMetadataExtracter.class);
private List<ContentWorkerSelector<MetadataExtracter>> selectors; private List<ContentWorkerSelector<MetadataExtracter>> selectors;
/** /**
@@ -68,11 +70,11 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
} }
/** /**
* Sets the list of metadata selectors to use to find the extracter to use, given * Sets the list of metadata selectors to use to find the extracter to use,
* some content. The evaluations are done in the order that they occur in the * given some content. The evaluations are done in the order that they occur
* list. * in the list.
* *
* @param selectors A list of selectors * @param selectors A list of selectors
*/ */
public void setSelectors(List<ContentWorkerSelector<MetadataExtracter>> selectors) public void setSelectors(List<ContentWorkerSelector<MetadataExtracter>> selectors)
{ {
@@ -88,9 +90,10 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
} }
/** /**
* It is not possible to have any default mappings, but something has to be returned. * It is not possible to have any default mappings, but something has to be
* returned.
* *
* @return Returns an empty map * @return Returns an empty map
*/ */
@Override @Override
protected Map<String, Set<QName>> getDefaultMapping() protected Map<String, Set<QName>> getDefaultMapping()
@@ -102,25 +105,22 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
* Selects and extracter to perform the work and redirects to it. * Selects and extracter to perform the work and redirects to it.
*/ */
@Override @Override
public Map<QName, Serializable> extract( public Map<QName, Serializable> extract(ContentReader reader, OverwritePolicy overwritePolicy,
ContentReader reader, Map<QName, Serializable> destination, Map<String, Set<QName>> mapping)
OverwritePolicy overwritePolicy,
Map<QName, Serializable> destination,
Map<String, Set<QName>> mapping)
{ {
// Check the content length // Check the content length
if (reader.getSize() == 0) if (reader.getSize() == 0)
{ {
// There is no content. We don't spoof any properties so there can be nothing extracted. // There is no content. We don't spoof any properties so there can
// be nothing extracted.
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("\n" + logger.debug("\n" + "XML document has zero length, so bypassing extraction: \n" + " Document: "
"XML document has zero length, so bypassing extraction: \n" + + reader);
" Document: " + reader);
} }
return destination; return destination;
} }
MetadataExtracter extracter = null; MetadataExtracter extracter = null;
// Select a worker // Select a worker
for (ContentWorkerSelector<MetadataExtracter> selector : selectors) for (ContentWorkerSelector<MetadataExtracter> selector : selectors)
@@ -138,9 +138,8 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
{ {
if (reader.isChannelOpen()) if (reader.isChannelOpen())
{ {
logger.error("Content reader not closed by MetadataExtractor selector: \n" + logger.error("Content reader not closed by MetadataExtractor selector: \n" + " reader: "
" reader: " + reader + "\n" + + reader + "\n" + " selector: " + selector);
" selector: " + selector);
} }
} }
// Just take the first successful one // Just take the first successful one
@@ -148,10 +147,8 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("\n" + logger.debug("\n" + "Found metadata extracter to process XML document: \n" + " Selector: "
"Found metadata extracter to process XML document: \n" + + selector + "\n" + " Document: " + reader);
" Selector: " + selector + "\n" +
" Document: " + reader);
} }
break; break;
} }
@@ -162,9 +159,7 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("\n" + logger.debug("\n" + "No working metadata extractor could be found: \n" + " Document: " + reader);
"No working metadata extractor could be found: \n" +
" Document: " + reader);
} }
// There will be no properties extracted // There will be no properties extracted
modifiedProperties = destination; modifiedProperties = destination;
@@ -180,26 +175,22 @@ public class XmlMetadataExtracter extends AbstractMappingMetadataExtracter
{ {
if (reader.isChannelOpen()) if (reader.isChannelOpen())
{ {
logger.error("Content reader not closed by MetadataExtractor: \n" + logger.error("Content reader not closed by MetadataExtractor: \n" + " Reader: " + reader + "\n"
" Reader: " + reader + "\n" + + " extracter: " + extracter);
" extracter: " + extracter);
} }
} }
} }
// Done // Done
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("\n" + logger.debug("\n" + "XML metadata extractor redirected: \n" + " Reader: " + reader + "\n"
"XML metadata extractor redirected: \n" + + " Extracter: " + extracter + "\n" + " Metadata: " + modifiedProperties);
" Reader: " + reader + "\n" +
" Extracter: " + extracter + "\n" +
" Metadata: " + modifiedProperties);
} }
return modifiedProperties; return modifiedProperties;
} }
/** /**
* This is not required as the * This is not required as the
*/ */
protected Map<String, Serializable> extractRaw(ContentReader reader) throws Throwable protected Map<String, Serializable> extractRaw(ContentReader reader) throws Throwable
{ {

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2005-2010 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.content.transform.magick;
/**
* DTO used to store options for ImageMagick cropping.
*
* @author Nick Smith
*/
public class ImageCropOptions
{
private int height = -1;
private int width = -1;
private int xOffset = 0;
private int yOffset = 0;
private boolean isPercentageCrop = false;
private String gravity = null;
/**
* Gets the height of the cropped image. By default this is in pixels but if
* <code>isPercentageCrop</code> is set to true then it changes to
* percentage.
*
* @return the height
*/
public int getHeight()
{
return this.height;
}
/**
* Sets the height of the cropped image. By default this is in pixels but if
* <code>isPercentageCrop</code> is set to true then it changes to
* percentage.
*
* @param height the height to set
*/
public void setHeight(int height)
{
this.height = height;
}
/**
* Sets the width of the cropped image. By default this is in pixels but if
* <code>isPercentageCrop</code> is set to true then it changes to
* percentage.
*
* @return the width
*/
public int getWidth()
{
return this.width;
}
/**
* Sets the width of the cropped image. By default this is in pixels but if
* <code>isPercentageCrop</code> is set to true then it changes to
* percentage.
*
* @param width the width to set
*/
public void setWidth(int width)
{
this.width = width;
}
/**
* Gets the horizontal offset. By default this starts fromt he top-left
* corner of the image and moves right, however the <code>gravity</code>
* property can change this.
*
* @return the xOffset
*/
public int getXOffset()
{
return this.xOffset;
}
/**
* Sets the horizontal offset. By default this starts fromt he top-left
* corner of the image and moves right, however the <code>gravity</code>
* property can change this.
*
* @param xOffset the xOffset to set
*/
public void setXOffset(int xOffset)
{
this.xOffset = xOffset;
}
/**
* Gets the vertical offset. By default this starts fromt he top-left corner
* of the image and moves down, however the <code>gravity</code> property
* can change this.
*
* @return the yOffset
*/
public int getYOffset()
{
return this.yOffset;
}
/**
* Sets the vertical offset. By default this starts fromt he top-left corner
* of the image and moves down, however the <code>gravity</code> property
* can change this.
*
* @param yOffset the yOffset to set
*/
public void setYOffset(int yOffset)
{
this.yOffset = yOffset;
}
/**
* @return the isPercentageCrop
*/
public boolean isPercentageCrop()
{
return this.isPercentageCrop;
}
/**
* @param isPercentageCrop the isPercentageCrop to set
*/
public void setPercentageCrop(boolean isPercentageCrop)
{
this.isPercentageCrop = isPercentageCrop;
}
/**
* Sets the 'gravity' which determines how the offset is applied. It affects
* both the origin of offset and the direction(s).
*
* @param gravity the gravity to set
*/
public void setGravity(String gravity)
{
this.gravity = gravity;
}
/**
* Gets the 'gravity' which determines how the offset is applied. It affects
* both the origin of offset and the direction(s).
*
* @return the gravity
*/
public String getGravity()
{
return this.gravity;
}
}

View File

@@ -149,8 +149,13 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont
if (options instanceof ImageTransformationOptions) if (options instanceof ImageTransformationOptions)
{ {
ImageTransformationOptions imageOptions = (ImageTransformationOptions)options; ImageTransformationOptions imageOptions = (ImageTransformationOptions)options;
ImageCropOptions cropOptions = imageOptions.getCropOptions();
ImageResizeOptions resizeOptions = imageOptions.getResizeOptions(); ImageResizeOptions resizeOptions = imageOptions.getResizeOptions();
String commandOptions = imageOptions.getCommandOptions(); String commandOptions = imageOptions.getCommandOptions();
if (cropOptions != null)
{
commandOptions = commandOptions + " " + getImageCropCommandOptions(cropOptions);
}
if (resizeOptions != null) if (resizeOptions != null)
{ {
commandOptions = commandOptions + " " + getImageResizeCommandOptions(resizeOptions); commandOptions = commandOptions + " " + getImageResizeCommandOptions(resizeOptions);
@@ -173,6 +178,59 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont
} }
} }
/**
* Gets the imagemagick command string for the image crop options provided
*
* @param imageResizeOptions image resize options
* @return String the imagemagick command options
*/
private String getImageCropCommandOptions(ImageCropOptions cropOptions)
{
StringBuilder builder = new StringBuilder(32);
String gravity = cropOptions.getGravity();
if(gravity!=null)
{
builder.append("-gravity ");
builder.append(gravity);
builder.append(" ");
}
builder.append("-crop ");
int width = cropOptions.getWidth();
if (width > -1)
{
builder.append(width);
}
int height = cropOptions.getHeight();
if (height > -1)
{
builder.append("x");
builder.append(height);
}
if (cropOptions.isPercentageCrop())
{
builder.append("%");
}
appendOffset(builder, cropOptions.getXOffset());
appendOffset(builder, cropOptions.getYOffset());
builder.append(" +repage");
return builder.toString();
}
/**
* @param builder
* @param xOffset
*/
private void appendOffset(StringBuilder builder, int xOffset)
{
if(xOffset>=0)
{
builder.append("+");
}
builder.append(xOffset);
}
/** /**
* Gets the imagemagick command string for the image resize options provided * Gets the imagemagick command string for the image resize options provided
* *

View File

@@ -33,6 +33,9 @@ public class ImageTransformationOptions extends TransformationOptions
/** Image resize options */ /** Image resize options */
private ImageResizeOptions resizeOptions; private ImageResizeOptions resizeOptions;
/** Image crop options */
private ImageCropOptions cropOptions;
/** /**
* Set the command string options * Set the command string options
* *
@@ -84,4 +87,20 @@ public class ImageTransformationOptions extends TransformationOptions
return msg.toString(); return msg.toString();
} }
/**
* @param cropOptions the cropOptions to set
*/
public void setCropOptions(ImageCropOptions cropOptions)
{
this.cropOptions = cropOptions;
}
/**
* @return the cropOptions
*/
public ImageCropOptions getCropOptions()
{
return this.cropOptions;
}
} }

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.repo.thumbnail.ThumbnailServiceImplParameterTest;
import org.alfresco.repo.thumbnail.ThumbnailServiceImplTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* This class is a holder for the various test classes associated with the Rendition Service.
* It is not (at the time of writing) intended to be incorporated into the automatic build
* which will find the various test classes and run them individually.
*
* @author Neil McErlean
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({
RenditionServiceImplTest.class,
ThumbnailServiceImplParameterTest.class,
ThumbnailServiceImplTest.class,
StandardRenditionLocationResolverTest.class,
RenditionServiceIntegrationTest.class,
RenditionServicePermissionsTest.class
})
public class AllRenditionTests
{
// Intentionally empty
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.util.List;
import org.alfresco.repo.action.ActionListImpl;
import org.alfresco.repo.rendition.executer.CompositeRenderingEngine;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionList;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
*/
public class CompositeRenditionDefinitionImpl extends RenditionDefinitionImpl implements CompositeRenditionDefinition
{
/**
* Serial Version UID
*/
private static final long serialVersionUID = -770880495976834168L;
private final ActionList<RenditionDefinition> actions = new ActionListImpl<RenditionDefinition>();
/**
* @param nodeRef
* @param id
*/
public CompositeRenditionDefinitionImpl(String id, QName renditionName)
{
super(id, renditionName, CompositeRenderingEngine.NAME);
}
public CompositeRenditionDefinitionImpl(CompositeAction compositeAction)
{
super(compositeAction, CompositeRenderingEngine.NAME);
for (Action action : compositeAction.getActions())
{
RenditionDefinition subDefinition;
if (action instanceof CompositeAction)
{
CompositeAction compAction = (CompositeAction) action;
subDefinition = new CompositeRenditionDefinitionImpl(compAction);
}
else
{
subDefinition = new RenditionDefinitionImpl(action);
}
addAction(subDefinition);
}
}
/**
* @param index
* @param action
* @see org.alfresco.service.cmr.action.ActionList#addAction(int,
* org.alfresco.service.cmr.action.Action)
*/
public void addAction(int index, RenditionDefinition action)
{
this.actions.addAction(index, action);
}
/**
* @param action
* @see org.alfresco.service.cmr.action.ActionList#addAction(org.alfresco.service.cmr.action.Action)
*/
public void addAction(RenditionDefinition action)
{
this.actions.addAction(action);
}
/**
* @param index
* @return
* @see org.alfresco.service.cmr.action.ActionList#getAction(int)
*/
public RenditionDefinition getAction(int index)
{
return this.actions.getAction(index);
}
/**
* @return
* @see org.alfresco.service.cmr.action.ActionList#getActions()
*/
public List<RenditionDefinition> getActions()
{
return this.actions.getActions();
}
/**
* @return
* @see org.alfresco.service.cmr.action.ActionList#hasActions()
*/
public boolean hasActions()
{
return this.actions.hasActions();
}
/**
* @param action
* @return
* @see org.alfresco.service.cmr.action.ActionList#indexOfAction(org.alfresco.service.cmr.action.Action)
*/
public int indexOfAction(RenditionDefinition action)
{
return this.actions.indexOfAction(action);
}
/**
* @param action
* @see org.alfresco.service.cmr.action.ActionList#removeAction(org.alfresco.service.cmr.action.Action)
*/
public void removeAction(RenditionDefinition action)
{
this.actions.removeAction(action);
}
/**
* @see org.alfresco.service.cmr.action.ActionList#removeAllActions()
*/
public void removeAllActions()
{
this.actions.removeAllActions();
}
/**
* @param index
* @param action
* @see org.alfresco.service.cmr.action.ActionList#setAction(int,
* org.alfresco.service.cmr.action.Action)
*/
public void setAction(int index, RenditionDefinition action)
{
this.actions.setAction(index, action);
}
}

View File

@@ -0,0 +1,485 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import static org.mockito.Mockito.mock;
import java.util.Collection;
import org.alfresco.cmis.CMISDictionaryService;
import org.alfresco.cmis.CMISQueryService;
import org.alfresco.cmis.CMISServices;
import org.alfresco.mbeans.VirtServerRegistry;
import org.alfresco.repo.forms.FormService;
import org.alfresco.repo.imap.ImapService;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.audit.AuditService;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avm.deploy.DeploymentService;
import org.alfresco.service.cmr.avm.locking.AVMLockingService;
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.invitation.InvitationService;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.ml.ContentFilterLanguagesService;
import org.alfresco.service.cmr.ml.EditionService;
import org.alfresco.service.cmr.ml.MultilingualContentService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.CopyService;
import org.alfresco.service.cmr.repository.CrossRepositoryCopyService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.search.CategoryService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.security.PublicServiceAccessService;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.cmr.view.ExporterService;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.wcm.asset.AssetService;
import org.alfresco.wcm.preview.PreviewURIService;
import org.alfresco.wcm.sandbox.SandboxService;
import org.alfresco.wcm.webproject.WebProjectService;
public class MockedTestServiceRegistry implements ServiceRegistry
{
private final ActionService actionService = mock(ActionService.class);
private final ContentService contentService = mock(ContentService.class);
private final NodeService nodeService = mock(NodeService.class);
private final TemplateService templateService = mock(TemplateService.class);
private final PersonService personService = mock(PersonService.class);
private final MutableAuthenticationService authenticationService = mock(MutableAuthenticationService.class);
private final NamespaceService namespaceService = mock(NamespaceService.class);
public boolean isServiceProvided(QName service)
{
// TODO Auto-generated method stub
return false;
}
public WorkflowService getWorkflowService()
{
// TODO Auto-generated method stub
return null;
}
public WebProjectService getWebProjectService()
{
// TODO Auto-generated method stub
return null;
}
public VirtServerRegistry getVirtServerRegistry()
{
// TODO Auto-generated method stub
return null;
}
public VersionService getVersionService()
{
// TODO Auto-generated method stub
return null;
}
public TransactionService getTransactionService()
{
// TODO Auto-generated method stub
return null;
}
public ThumbnailService getThumbnailService()
{
// TODO Auto-generated method stub
return null;
}
public TemplateService getTemplateService()
{
return this.templateService;
}
public TaggingService getTaggingService()
{
// TODO Auto-generated method stub
return null;
}
public SiteService getSiteService()
{
// TODO Auto-generated method stub
return null;
}
public Collection<QName> getServices()
{
// TODO Auto-generated method stub
return null;
}
public Object getService(QName service)
{
// TODO Auto-generated method stub
return null;
}
public SearchService getSearchService()
{
// TODO Auto-generated method stub
return null;
}
public ScriptService getScriptService()
{
// TODO Auto-generated method stub
return null;
}
public SandboxService getSandboxService()
{
// TODO Auto-generated method stub
return null;
}
public RuleService getRuleService()
{
// TODO Auto-generated method stub
return null;
}
public RetryingTransactionHelper getRetryingTransactionHelper()
{
// TODO Auto-generated method stub
return null;
}
public PublicServiceAccessService getPublicServiceAccessService()
{
// TODO Auto-generated method stub
return null;
}
public PreviewURIService getPreviewURIService()
{
// TODO Auto-generated method stub
return null;
}
public PersonService getPersonService()
{
return personService;
}
public PermissionService getPermissionService()
{
// TODO Auto-generated method stub
return null;
}
public OwnableService getOwnableService()
{
// TODO Auto-generated method stub
return null;
}
public NodeService getNodeService()
{
return nodeService;
}
public NamespaceService getNamespaceService()
{
// TODO Auto-generated method stub
return namespaceService;
}
public MultilingualContentService getMultilingualContentService()
{
// TODO Auto-generated method stub
return null;
}
public MimetypeService getMimetypeService()
{
// TODO Auto-generated method stub
return null;
}
public LockService getLockService()
{
// TODO Auto-generated method stub
return null;
}
public JobLockService getJobLockService()
{
// TODO Auto-generated method stub
return null;
}
public InvitationService getInvitationService()
{
// TODO Auto-generated method stub
return null;
}
public ImporterService getImporterService()
{
// TODO Auto-generated method stub
return null;
}
public ImapService getImapService()
{
// TODO Auto-generated method stub
return null;
}
public FormService getFormService()
{
// TODO Auto-generated method stub
return null;
}
public FileFolderService getFileFolderService()
{
// TODO Auto-generated method stub
return null;
}
public ExporterService getExporterService()
{
// TODO Auto-generated method stub
return null;
}
public EditionService getEditionService()
{
// TODO Auto-generated method stub
return null;
}
public DictionaryService getDictionaryService()
{
// TODO Auto-generated method stub
return null;
}
public DescriptorService getDescriptorService()
{
// TODO Auto-generated method stub
return null;
}
public DeploymentService getDeploymentService()
{
// TODO Auto-generated method stub
return null;
}
public CrossRepositoryCopyService getCrossRepositoryCopyService()
{
// TODO Auto-generated method stub
return null;
}
public CopyService getCopyService()
{
// TODO Auto-generated method stub
return null;
}
public ContentService getContentService()
{
// TODO Auto-generated method stub
return contentService;
}
public ContentFilterLanguagesService getContentFilterLanguagesService()
{
// TODO Auto-generated method stub
return null;
}
public CheckOutCheckInService getCheckOutCheckInService()
{
// TODO Auto-generated method stub
return null;
}
public CategoryService getCategoryService()
{
// TODO Auto-generated method stub
return null;
}
public CMISServices getCMISService()
{
// TODO Auto-generated method stub
return null;
}
public CMISQueryService getCMISQueryService()
{
// TODO Auto-generated method stub
return null;
}
public CMISDictionaryService getCMISDictionaryService()
{
// TODO Auto-generated method stub
return null;
}
public AuthorityService getAuthorityService()
{
// TODO Auto-generated method stub
return null;
}
public MutableAuthenticationService getAuthenticationService()
{
// TODO Auto-generated method stub
return authenticationService;
}
public AuditService getAuditService()
{
// TODO Auto-generated method stub
return null;
}
public AttributeService getAttributeService()
{
// TODO Auto-generated method stub
return null;
}
public AssetService getAssetService()
{
// TODO Auto-generated method stub
return null;
}
public ActionService getActionService()
{
// TODO Auto-generated method stub
return actionService;
}
public AVMSyncService getAVMSyncService()
{
// TODO Auto-generated method stub
return null;
}
public AVMService getAVMService()
{
// TODO Auto-generated method stub
return null;
}
public AVMLockingService getAVMLockingService()
{
// TODO Auto-generated method stub
return null;
}
public AVMService getAVMLockingAwareService()
{
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,518 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import static org.alfresco.model.ContentModel.PROP_NODE_DBID;
import static org.alfresco.model.ContentModel.PROP_NODE_REF;
import static org.alfresco.model.ContentModel.PROP_NODE_UUID;
import static org.alfresco.model.ContentModel.PROP_STORE_IDENTIFIER;
import static org.alfresco.model.ContentModel.PROP_STORE_NAME;
import static org.alfresco.model.ContentModel.PROP_STORE_PROTOCOL;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuter;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.NodeLocator;
import org.alfresco.service.cmr.rendition.RenderCallback;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.ParameterCheck;
/*
* This class is the action executer for the perform-rendition action. All renditions are
* executed by this class as wrapping them all in a containing action facilitates
* asynchronous renditions.
* <P/>
* Some of the logic is executed directly by this class (handling of rendition-related
* aspects and associations) and the rest is executed by subordinate actions called
* from within this action (the actual rendering code). These subordinate actions are
* renditionDefinitions.
*
* @author Neil McErlean
* @since 3.3
*/
public class PerformRenditionActionExecuter extends ActionExecuterAbstractBase
{
private static final Log log = LogFactory.getLog(PerformRenditionActionExecuter.class);
/** Action name and parameters */
public static final String NAME = "perform-rendition";
public static final String PARAM_RENDITION_DEFINITION = "renditionDefinition";
private static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName();
private static final List<QName> unchangedProperties = Arrays.asList(PROP_NODE_DBID, PROP_NODE_REF, PROP_NODE_UUID,
PROP_STORE_IDENTIFIER, PROP_STORE_NAME, PROP_STORE_PROTOCOL);
/**
* Default {@link NodeLocator} simply returns the source node.
*/
private final static NodeLocator defaultNodeLocator = new NodeLocator()
{
public NodeRef getNode(NodeRef sourceNode, Map<String, Serializable> params)
{
return sourceNode;
}
};
/*
* Injected beans
*/
private RenditionLocationResolver renditionLocationResolver;
private ActionService actionService;
private NodeService nodeService;
private RenditionService renditionService;
private final NodeLocator temporaryParentNodeLocator;
private final QName temporaryRenditionLinkType;
public PerformRenditionActionExecuter(NodeLocator temporaryParentNodeLocator, QName temporaryRenditionLinkType)
{
this.temporaryParentNodeLocator = temporaryParentNodeLocator != null ? temporaryParentNodeLocator
: defaultNodeLocator;
this.temporaryRenditionLinkType = temporaryRenditionLinkType != null ? temporaryRenditionLinkType
: RenditionModel.ASSOC_RENDITION;
}
public PerformRenditionActionExecuter()
{
this(null, null);
}
/**
* Injects the actionService bean.
*
* @param actionService
* the actionService.
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Injects the nodeService bean.
*
* @param nodeService
* the nodeService.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Injects the renditionService bean.
*
* @param renditionService
*/
public void setRenditionService(RenditionService renditionService)
{
this.renditionService = renditionService;
}
public void setRenditionLocationResolver(RenditionLocationResolver renditionLocationResolver)
{
this.renditionLocationResolver = renditionLocationResolver;
}
@Override
protected void executeImpl(final Action containingAction, final NodeRef actionedUponNodeRef)
{
final RenditionDefinition renditionDefinition = getRenditionDefinition(containingAction);
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Rendering node ").append(actionedUponNodeRef).append(" with rendition definition ").append(
renditionDefinition.getRenditionName());
log.debug(msg.toString());
}
Serializable runAsParam = renditionDefinition.getParameterValue(AbstractRenderingEngine.PARAM_RUN_AS);
String runAsName = runAsParam == null ? DEFAULT_RUN_AS_NAME : (String) runAsParam;
// Renditions should all be created by system by default.
// When renditions are created by a user and are to be created under a
// node
// other than the source node, it is possible that the user will not
// have
// permissions to create content under that node.
// For that reason, we execute all perform-rendition actions as system
// by default.
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
{
public Void doWork() throws Exception
{
ChildAssociationRef result = null;
try
{
setTemporaryRenditionProperties(actionedUponNodeRef, renditionDefinition);
// Adds the 'Renditioned' aspect to the source node if it
// doesn't exist.
if (!nodeService.hasAspect(actionedUponNodeRef, RenditionModel.ASPECT_RENDITIONED))
{
nodeService.addAspect(actionedUponNodeRef, RenditionModel.ASPECT_RENDITIONED, null);
}
ChildAssociationRef tempRendAssoc = executeRendition(actionedUponNodeRef, renditionDefinition);
result = createOrUpdateRendition(actionedUponNodeRef, tempRendAssoc, renditionDefinition);
containingAction.setParameterValue(PARAM_RESULT, result);
} catch (Throwable t)
{
notifyCallbackOfException(renditionDefinition, t);
throwWrappedException(t);
}
if (result != null)
{
notifyCallbackOfResult(renditionDefinition, result);
}
return null;
}
}, runAsName);
}
private RenditionDefinition getRenditionDefinition(final Action containingAction)
{
Serializable rendDefObj = containingAction.getParameterValue(PARAM_RENDITION_DEFINITION);
ParameterCheck.mandatory(PARAM_RENDITION_DEFINITION, rendDefObj);
return (RenditionDefinition) rendDefObj;
}
// Rendition has failed. If there is a callback, it needs to be notified
private void notifyCallbackOfException(RenditionDefinition renditionDefinition, Throwable t)
{
if (renditionDefinition != null)
{
RenderCallback callback = renditionDefinition.getCallback();
if (callback != null)
{
callback.handleFailedRendition(t);
}
}
}
// and rethrow Exception
private void throwWrappedException(Throwable t)
{
if (t instanceof AlfrescoRuntimeException)
{
throw (AlfrescoRuntimeException) t;
} else
{
throw new RenditionServiceException(t.getMessage(), t);
}
}
private void notifyCallbackOfResult(RenditionDefinition renditionDefinition, ChildAssociationRef result)
{
// Rendition was successful. Notify the callback object.
if (renditionDefinition != null)
{
RenderCallback callback = renditionDefinition.getCallback();
if (callback != null)
{
callback.handleSuccessfulRendition(result);
}
}
}
/**
* This method sets the renditionParent and rendition assocType.
*
* @param sourceNode
* @param definition
*/
private void setTemporaryRenditionProperties(NodeRef sourceNode, RenditionDefinition definition)
{
// Set the parent and assoc type for the temporary rendition to be
// created.
NodeRef parent = temporaryParentNodeLocator.getNode(sourceNode, definition.getParameterValues());
definition.setRenditionParent(parent);
definition.setRenditionAssociationType(temporaryRenditionLinkType);
}
/**
* @param sourceNode
* @param definition
* @return
*/
private ChildAssociationRef executeRendition(NodeRef sourceNode, RenditionDefinition definition)
{
actionService.executeAction(definition, sourceNode);
// Extract the result from the action
Serializable serializableResult = definition.getParameterValue(ActionExecuter.PARAM_RESULT);
return (ChildAssociationRef) serializableResult;
}
private ChildAssociationRef createOrUpdateRendition(NodeRef sourceNode, ChildAssociationRef tempRendition,
RenditionDefinition renditionDefinition)
{
NodeRef tempRenditionNode = tempRendition.getChildRef();
RenditionLocation location = getDestinationParentAssoc(sourceNode, renditionDefinition, tempRenditionNode);
QName renditionQName = renditionDefinition.getRenditionName();
if (log.isDebugEnabled())
{
final String lineBreak = System.getProperty("line.separator", "\n");
StringBuilder msg = new StringBuilder();
msg.append("Creating/updating rendition based on:").append(lineBreak).append(" sourceNode: ").append(
sourceNode).append(lineBreak).append(" tempRendition: ").append(tempRendition).append(
lineBreak).append(" parentNode: ").append(location.getParentRef()).append(lineBreak).append(
" childName: ").append(location.getChildName()).append(lineBreak).append(
" renditionDefinition.name: ").append(renditionQName);
log.debug(msg.toString());
}
ChildAssociationRef primaryAssoc = findOrCreatePrimaryRenditionAssociation(sourceNode, renditionDefinition,
location);
// Copy relevant properties from the temporary node to the new rendition
// node.
NodeRef renditionNode = primaryAssoc.getChildRef();
transferNodeProperties(tempRenditionNode, renditionNode);
// Set the name property on the rendition if it has not already been
// set.
String renditionName = getRenditionName(tempRenditionNode, location, renditionDefinition);
nodeService.setProperty(renditionNode, ContentModel.PROP_NAME, renditionName);
// Delete the temporary rendition.
nodeService.removeChildAssociation(tempRendition);
// Handle the rendition aspects
manageRenditionAspects(sourceNode, primaryAssoc);
ChildAssociationRef renditionAssoc = renditionService.getRenditionByName(sourceNode, renditionQName);
if (renditionAssoc == null)
{
String msg = "A rendition of type: " + renditionQName + " should have been created for source node: "
+ sourceNode;
throw new RenditionServiceException(msg);
}
return renditionAssoc;
}
private void manageRenditionAspects(NodeRef sourceNode, ChildAssociationRef renditionParentAssoc)
{
NodeRef renditionNode = renditionParentAssoc.getChildRef();
NodeRef primaryParent = renditionParentAssoc.getParentRef();
// If the rendition is located directly underneath its own source node
if (primaryParent.equals(sourceNode))
{
// It should be a 'hidden' rendition.
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_HIDDEN_RENDITION, null);
nodeService.removeAspect(renditionNode, RenditionModel.ASPECT_VISIBLE_RENDITION);
// We remove the other aspect to cover the potential case where a
// rendition
// has been updated in a different location.
} else
{
// Renditions stored underneath any node other than their source are
// 'visible'.
nodeService.addAspect(renditionNode, RenditionModel.ASPECT_VISIBLE_RENDITION, null);
nodeService.removeAspect(renditionNode, RenditionModel.ASPECT_HIDDEN_RENDITION);
}
}
private String getRenditionName(NodeRef tempRenditionNode, RenditionLocation location,
RenditionDefinition renditionDefinition)
{
// If a location name is set then use it.
String locName = location.getChildName();
if (locName != null && locName.length() > 0)
{
return locName;
}
// Else if the temporary rendition specifies a name property use that.
Serializable tempName = nodeService.getProperty(tempRenditionNode, ContentModel.PROP_NAME);
if (tempName != null)
{
return (String) tempName;
}
// Otherwise use the rendition definition local name.
return renditionDefinition.getRenditionName().getLocalName();
}
private ChildAssociationRef findOrCreatePrimaryRenditionAssociation(NodeRef sourceNode,
RenditionDefinition renditionDefinition, RenditionLocation location)
{
// Get old Rendition if exists.
QName renditionName = renditionDefinition.getRenditionName();
ChildAssociationRef oldRenditionAssoc = renditionService.getRenditionByName(sourceNode, renditionName);
// If no rendition already exists create anew rendition node and
// association.
if (oldRenditionAssoc == null)
{
return getSpecifiedRenditionOrCreateNewRendition(sourceNode, location, renditionName);
}
// If a rendition exists and is already in the correct location then
// return that renditions primary parent association
NodeRef oldRendition = oldRenditionAssoc.getChildRef();
if (renditionLocationMatches(oldRendition, location))
{
return nodeService.getPrimaryParent(oldRendition);
}
// If the old rendition is in the wrong location and the 'orphan
// existing rendition' param is set to true or the RenditionLocation
// specifies a destination NodeRef then ldelete the old
// rendition association and create a new rendition node.
if (orphanExistingRendition(renditionDefinition, location))
{
orphanRendition(oldRenditionAssoc);
return getSpecifiedRenditionOrCreateNewRendition(sourceNode, location, renditionName);
}
// If the old rendition is in the wrong place and the 'orphan existing
// rendition' param is not set to true then move the existing rendition
// to the correct location.
return moveRendition(oldRendition, location, renditionName);
}
private ChildAssociationRef moveRendition(NodeRef renditionNode, RenditionLocation location, QName associationName)
{
ChildAssociationRef assoc = nodeService.moveNode(renditionNode, location.getParentRef(),
ContentModel.ASSOC_CONTAINS, associationName);
return assoc;
}
private void orphanRendition(ChildAssociationRef oldRenditionAssoc)
{
NodeRef oldRendition = oldRenditionAssoc.getChildRef();
nodeService.removeAspect(oldRendition, RenditionModel.ASPECT_HIDDEN_RENDITION);
nodeService.removeAspect(oldRendition, RenditionModel.ASPECT_VISIBLE_RENDITION);
nodeService.removeChildAssociation(oldRenditionAssoc);
}
private boolean orphanExistingRendition(RenditionDefinition renditionDefinition, RenditionLocation location)
{
if (location.getChildRef() != null)
return true;
else
return AbstractRenderingEngine.getParamWithDefault(RenditionService.PARAM_ORPHAN_EXISTING_RENDITION,
Boolean.FALSE, renditionDefinition);
}
private boolean renditionLocationMatches(NodeRef oldRendition, RenditionLocation location)
{
NodeRef destination = location.getChildRef();
if (destination != null)
{
return destination.equals(oldRendition);
}
ChildAssociationRef oldParentAssoc = nodeService.getPrimaryParent(oldRendition);
NodeRef oldParent = oldParentAssoc.getParentRef();
if (oldParent.equals(location.getParentRef()))
{
String childName = location.getChildName();
if (childName == null)
return true;
else
{
Serializable oldName = nodeService.getProperty(oldRendition, ContentModel.PROP_NAME);
return childName.equals(oldName);
}
}
return false;
}
private ChildAssociationRef getSpecifiedRenditionOrCreateNewRendition(NodeRef sourceNode,
RenditionLocation location, QName renditionName)
{
NodeRef destination = location.getChildRef();
if (destination != null)
return nodeService.getPrimaryParent(destination);
else
return createNewRendition(sourceNode, location, renditionName);
}
private ChildAssociationRef createNewRendition(NodeRef sourceNode, RenditionLocation location, QName renditionName)
{
NodeRef parentRef = location.getParentRef();
boolean parentIsSource = parentRef.equals(sourceNode);
QName renditionType = RenditionModel.ASSOC_RENDITION;
QName assocTypeQName = parentIsSource ? renditionType : ContentModel.ASSOC_CONTAINS;
QName nodeTypeQName = ContentModel.TYPE_CONTENT;
ChildAssociationRef primaryAssoc = nodeService.createNode(parentRef, assocTypeQName, renditionName,
nodeTypeQName);
// If the new rendition is not directly under the source node then add
// the rendition association.
if (parentIsSource == false)
{
NodeRef rendition = primaryAssoc.getChildRef();
nodeService.addChild(sourceNode, rendition, renditionType, renditionName);
}
return primaryAssoc;
}
/**
* @param sourceNode
* @param targetNode
*/
private void transferNodeProperties(NodeRef sourceNode, NodeRef targetNode)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Transferring some properties from ").append(sourceNode).append(" to ").append(targetNode);
log.debug(msg.toString());
}
Map<QName, Serializable> newProps = nodeService.getProperties(sourceNode);
for (QName propKey : unchangedProperties)
{
newProps.remove(propKey);
}
nodeService.setProperties(targetNode, newProps);
}
private RenditionLocation getDestinationParentAssoc(NodeRef sourceNode, RenditionDefinition definition,
NodeRef tempRendition)
{
return renditionLocationResolver.getRenditionLocation(sourceNode, definition, tempRendition);
}
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_RENDITION_DEFINITION, DataTypeDefinition.ANY, true,
getParamDisplayLabel(PARAM_RENDITION_DEFINITION)));
paramList.add(new ParameterDefinitionImpl(PARAM_RESULT, DataTypeDefinition.CHILD_ASSOC_REF, false,
getParamDisplayLabel(PARAM_RESULT)));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.repo.action.ActionDefinitionImpl;
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
/**
* @author Nick Smith
* @since 3.3
*/
public class RenderingEngineDefinitionImpl extends ActionDefinitionImpl implements RenderingEngineDefinition
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 1L;
public RenderingEngineDefinitionImpl(String name)
{
super(name);
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.rendition.RenderCallback;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
* @author Neil McErlean
* @since 3.3
*/
public class RenditionDefinitionImpl extends ActionImpl implements RenditionDefinition
{
/**
* Serial version UID
*/
private static final long serialVersionUID = 4336392868488634875L;
protected static final String RENDITION_DEFINITION_NAME = "renderingActionName";
public NodeRef renditionParent;
public QName renditionAssociationType;
private RenderCallback renderCallback;
/**
* @param id the action id
* @param renditionName a unique name for the rendering action.
* @param renderingEngineName the name of the rendering action definition
*/
public RenditionDefinitionImpl(String id, QName renditionName, String renderingEngineName)
{
super(null, id, renderingEngineName);
setParameterValue(RENDITION_DEFINITION_NAME, renditionName);
}
public RenditionDefinitionImpl(Action action)
{
super(action);
}
public RenditionDefinitionImpl(Action action, String renderingEngineName)
{
super(action, renderingEngineName);
}
/*
* @see
* org.alfresco.service.cmr.rendition.RenditionDefinition#getRenditionName()
*/
public QName getRenditionName()
{
return (QName) getParameterValue(RENDITION_DEFINITION_NAME);
}
/*
* @see
* org.alfresco.service.cmr.rendition.RenditionDefinition#getRenditionParent
* ()
*/
public NodeRef getRenditionParent()
{
return this.renditionParent;
}
/*
* @see
* org.alfresco.service.cmr.rendition.RenditionDefinition#setRenditionParent
* (org.alfresco.service.cmr.repository.NodeRef)
*/
public void setRenditionParent(NodeRef renditionParent)
{
this.renditionParent = renditionParent;
}
/*
* @seeorg.alfresco.service.cmr.rendition.RenditionDefinition#
* getRenditionAssociationType()
*/
public QName getRenditionAssociationType()
{
return this.renditionAssociationType;
}
/*
* @seeorg.alfresco.service.cmr.rendition.RenditionDefinition#
* setRenditionAssociationType(org.alfresco.service.namespace.QName)
*/
public void setRenditionAssociationType(QName renditionAssociationType)
{
this.renditionAssociationType = renditionAssociationType;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionDefinition#setCallback(org.alfresco.service.cmr.rendition.RenderCallback)
*/
public void setCallback(RenderCallback callback)
{
this.renderCallback = callback;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionDefinition#setCallback(org.alfresco.service.cmr.rendition.RenderCallback)
*/
public RenderCallback getCallback()
{
return this.renderCallback;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.util.List;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.namespace.QName;
/**
* This class provides the implementation of RenditionDefinition persistence.
*
* @author Nick Smith
* @author Neil McErlean
* @since 3.3
*/
public interface RenditionDefinitionPersister
{
/**
* This method serializes the {@link RenditionDefinition} and stores it in
* the repository. {@link RenditionDefinition}s saved in this way may be
* retrieved using the <code>load()</code> method.
*
* @param renditionDefinition The {@link RenditionDefinition} to be
* persisted.
*/
void saveRenditionDefinition(RenditionDefinition renditionDefinition);
/**
* This method retrieves a {@link RenditionDefinition} that has been stored
* in the repository using the <code>save()</code> method. If no
* {@link RenditionDefinition} exists in the repository with the specified
* rendition name then this method returns null.
*
* @param renditionName The unique identifier used to specify the
* {@link RenditionDefinition} to retrieve.
* @return The specified {@link RenditionDefinition} or null.
*/
RenditionDefinition loadRenditionDefinition(QName renditionName);
/**
* This method retrieves the {@link RenditionDefinition}s that have been
* stored in the repository using the <code>save()</code> method.
* <P/>
* If there are no such {@link RenditionDefinition}s, an empty list is
* returned.
*
* @return The {@link RenditionDefinition}s.
*/
List<RenditionDefinition> loadRenditionDefinitions();
/**
* This method retrieves the stored {@link RenditionDefinition}s that have
* been registered for the specified rendering engine name.
* <P/>
* If there are no such rendering {@link RenditionDefinition}s, an empty
* list is returned.
*
* @param renderingEngineName the name of a rendering engine. This is
* usually the spring bean name.
* @return The {@link RenditionDefinition}s.
* @throws NullPointerException if the renderingEngineName is null.
* @see #saveRenditionDefinition(RenditionDefinition)
*/
List<RenditionDefinition> loadRenditionDefinitions(String renderingEngineName);
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionModel;
import org.alfresco.repo.action.RuntimeActionService;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
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.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
/**
* This class provides the implementation of RenditionDefinition persistence.
*
* @author Nick Smith
* @author Neil McErlean
* @since 3.3
*/
public class RenditionDefinitionPersisterImpl implements RenditionDefinitionPersister
{
/** Reference to the rendering action space node */
private static final StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
protected static final NodeRef RENDERING_ACTION_ROOT_NODE_REF = new NodeRef(SPACES_STORE, "rendering_actions_space");
/* Injected services */
private NodeService nodeService;
private RuntimeActionService runtimeActionService;
/**
* Injects the NodeService bean.
*
* @param nodeService the NodeService.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Injects the RuntimeActionService bean.
*
* @param runtimeActionService the RuntimeActionService.
*/
public void setRuntimeActionService(RuntimeActionService runtimeActionService)
{
this.runtimeActionService = runtimeActionService;
}
public List<RenditionDefinition> loadRenditionDefinitions()
{
checkRenderingActionRootNodeExists();
// Note that in the call to getChildAssocs below, only the specified
// types are included.
// Subtypes of the type action:action will not be returned.
Set<QName> actionTypes = new HashSet<QName>();
actionTypes.add(ActionModel.TYPE_ACTION);
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(RENDERING_ACTION_ROOT_NODE_REF, actionTypes);
List<RenditionDefinition> renderingActions = new ArrayList<RenditionDefinition>(childAssocs.size());
for (ChildAssociationRef actionAssoc : childAssocs)
{
Action nextAction = runtimeActionService.createAction(actionAssoc.getChildRef());
renderingActions.add(new RenditionDefinitionImpl(nextAction));
}
return renderingActions;
}
public List<RenditionDefinition> loadRenditionDefinitions(String renditionEngineName)
{
if (renditionEngineName == null)
{
throw new NullPointerException("Unexpected null renditionEngineName");
}
List<RenditionDefinition> allRenditionDefinitions = this.loadRenditionDefinitions();
List<RenditionDefinition> filteredRenditionDefinitions = new ArrayList<RenditionDefinition>();
for (RenditionDefinition renderAction : allRenditionDefinitions)
{
if (renditionEngineName.equals(renderAction.getActionDefinitionName()))
{
filteredRenditionDefinitions.add(renderAction);
}
}
return filteredRenditionDefinitions;
}
public RenditionDefinition loadRenditionDefinition(QName renderingActionName)
{
NodeRef actionNode = findActionNode(renderingActionName);
if (actionNode != null)
{
Action action = runtimeActionService.createAction(actionNode);
if (action instanceof CompositeAction)
{
CompositeAction compAction = (CompositeAction) action;
return new CompositeRenditionDefinitionImpl(compAction);
}
else
{
return new RenditionDefinitionImpl(action);
}
}
else
return null;
}
public void saveRenditionDefinition(RenditionDefinition renderingAction)
{
NodeRef actionNodeRef = findOrCreateActionNode(renderingAction);
// TODO Serialize using JSON content instead.
// The current serialization mechanism creates a complex content model
// structure which is verbose and a JSON-based approach using a simplified
// content model perhaps could offer performance improvements.
runtimeActionService.saveActionImpl(actionNodeRef, renderingAction);
}
private NodeRef findActionNode(QName renderingActionName)
{
checkRenderingActionRootNodeExists();
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(//
RENDERING_ACTION_ROOT_NODE_REF,//
ContentModel.ASSOC_CONTAINS,//
renderingActionName);
if (childAssocs.isEmpty())
{
return null;
}
else
{
if (childAssocs.size() > 1)
{//
throw new RenditionServiceException("Multiple rendering actions with the name: " + renderingActionName
+ " exist!");
}
return childAssocs.get(0).getChildRef();
}
}
private NodeRef findOrCreateActionNode(RenditionDefinition renderingAction)
{
QName actionName = renderingAction.getRenditionName();
NodeRef actionNode = findActionNode(actionName);
if (actionNode == null)
{
actionNode = runtimeActionService.createActionNodeRef(//
renderingAction,//
RENDERING_ACTION_ROOT_NODE_REF,//
ContentModel.ASSOC_CONTAINS,//
actionName);
}
return actionNode;
}
/**
* This method checks whether the folder containing Rendering Action nodes
* exists.
*
* @throws RenditionServiceException if the folder node does not exist.
*/
private void checkRenderingActionRootNodeExists()
{
if (nodeService.exists(RENDERING_ACTION_ROOT_NODE_REF) == false)
{
throw new RenditionServiceException("Unable to find rendering action root node.");
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.service.cmr.repository.NodeRef;
public interface RenditionLocation
{
NodeRef getParentRef();
NodeRef getChildRef();
String getChildName();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.service.cmr.repository.NodeRef;
public class RenditionLocationImpl implements RenditionLocation
{
private final NodeRef parentRef;
private final NodeRef childRef;
private final String childName;
public RenditionLocationImpl(NodeRef parentRef, NodeRef childRef, String childName)
{
this.parentRef = parentRef;
this.childRef = childRef;
this.childName = childName;
}
public String getChildName()
{
return childName;
}
public NodeRef getParentRef()
{
return parentRef;
}
public NodeRef getChildRef()
{
return childRef;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
public interface RenditionLocationResolver
{
RenditionLocation getRenditionLocation(NodeRef sourceNode, RenditionDefinition definition, NodeRef tempRenditionLocation);
}

View File

@@ -0,0 +1,406 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
import org.alfresco.service.cmr.rendition.RenderCallback;
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/*
* @author Nick Smith
* @author Neil McErlean
* @since 3.3
*/
public class RenditionServiceImpl implements RenditionService, RenditionDefinitionPersister
{
private static final Log log = LogFactory.getLog(RenditionServiceImpl.class);
private ActionService actionService;
private ContentService contentService;
private DictionaryService dictionaryService;
private NodeService nodeService;
private RenditionDefinitionPersisterImpl renditionDefinitionPersister;
/**
* Injects the RenditionDefinitionPersister bean.
* @param renditionDefinitionPersister
*/
public void setRenditionDefinitionPersister(RenditionDefinitionPersisterImpl renditionDefinitionPersister)
{
this.renditionDefinitionPersister = renditionDefinitionPersister;
}
/**
* Injects the ServiceRegistry bean.
* @param serviceRegistry
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.contentService = serviceRegistry.getContentService();
this.nodeService = serviceRegistry.getNodeService();
}
/**
* Injects the ActionService bean.
* @param actionService
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Injects the DictionaryService bean.
* @param dictionaryService
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenderingEngineDefinition(java.lang.String)
*/
public RenderingEngineDefinition getRenderingEngineDefinition(String name)
{
ActionDefinition actionDefinition = actionService.getActionDefinition(name);
if (actionDefinition instanceof RenderingEngineDefinition)
{
return (RenderingEngineDefinition) actionDefinition;
}
else
return null;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenderingEngineDefinitions()
*/
public List<RenderingEngineDefinition> getRenderingEngineDefinitions()
{
List<RenderingEngineDefinition> results = new ArrayList<RenderingEngineDefinition>();
List<ActionDefinition> actionDefs = actionService.getActionDefinitions();
for (ActionDefinition actionDef : actionDefs)
{
if (actionDef instanceof RenderingEngineDefinition)
{
RenderingEngineDefinition renderingDef = (RenderingEngineDefinition) actionDef;
results.add(renderingDef);
}
}
return results;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#createRenditionDefinition
* (org.alfresco.service.namespace.QName, java.lang.String)
*/
public RenditionDefinition createRenditionDefinition(QName renderingActionName, String actionDefinitionName)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Creating rendition definition ")
.append(renderingActionName)
.append(" ")
.append(actionDefinitionName);
log.debug(msg.toString());
}
return new RenditionDefinitionImpl(GUID.generate(), renderingActionName, actionDefinitionName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#createCompositeRenditionDefinition(org.alfresco.service.namespace.QName)
*/
public CompositeRenditionDefinition createCompositeRenditionDefinition(QName renditionName)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Creating composite rendition definition ")
.append(renditionName);
log.debug(msg.toString());
}
return new CompositeRenditionDefinitionImpl(GUID.generate(), renditionName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#render(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rendition.RenditionDefinition)
*/
public ChildAssociationRef render(NodeRef sourceNode, RenditionDefinition definition)
{
ChildAssociationRef result = createAndExecuteRenditionAction(sourceNode, definition, false);
return result;
}
public void render(NodeRef sourceNode, RenditionDefinition definition,
RenderCallback callback)
{
// The asynchronous render can't return a ChildAssociationRef as it is created
// asynchronously after this method returns.
definition.setCallback(callback);
createAndExecuteRenditionAction(sourceNode, definition, true);
return;
}
private ChildAssociationRef createAndExecuteRenditionAction(NodeRef sourceNode,
RenditionDefinition definition, boolean asynchronous)
{
if (log.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
if (asynchronous)
{
msg.append("Asynchronously");
}
else
{
msg.append("Synchronously");
}
msg.append(" rendering node ").append(sourceNode)
.append(" with ").append(definition.getRenditionName());
log.debug(msg.toString());
}
Action performRenditionAction = actionService.createAction(PerformRenditionActionExecuter.NAME);
performRenditionAction.setParameterValue(PerformRenditionActionExecuter.PARAM_RENDITION_DEFINITION, definition);
final boolean checkConditions = false;
actionService.executeAction(performRenditionAction, sourceNode, checkConditions, asynchronous);
ChildAssociationRef result = (ChildAssociationRef)performRenditionAction.getParameterValue(PerformRenditionActionExecuter.PARAM_RESULT);
return result;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#saveRenditionDefinition
* (org.alfresco.service.cmr.rendition.RenditionDefinition)
*/
public void saveRenditionDefinition(RenditionDefinition renderingAction)
{
this.renditionDefinitionPersister.saveRenditionDefinition(renderingAction);
}
/*
* @see
* org.alfresco.service.cmr.rendition.RenditionService#loadRenderingAction
* (org.alfresco.service.namespace.QName)
*/
public RenditionDefinition loadRenditionDefinition(QName renderingActionName)
{
return this.renditionDefinitionPersister.loadRenditionDefinition(renderingActionName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#loadRenditionDefinitions()
*/
public List<RenditionDefinition> loadRenditionDefinitions()
{
return this.renditionDefinitionPersister.loadRenditionDefinitions();
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#loadRenderingActions
* (java.lang.String)
*/
public List<RenditionDefinition> loadRenditionDefinitions(String renditionEngineName)
{
return this.loadRenditionDefinitions(renditionEngineName);
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#getRenditions(org
* .alfresco.service.cmr.repository.NodeRef)
*/
public List<ChildAssociationRef> getRenditions(NodeRef node)
{
List<ChildAssociationRef> result = new ArrayList<ChildAssociationRef>();
// Check that the node has the renditioned aspect applied
if (nodeService.hasAspect(node, RenditionModel.ASPECT_RENDITIONED) == true)
{
// Get all the renditions that match the given rendition name
result = nodeService.getChildAssocs(node, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL);
}
return result;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.rendition.RenditionService#getRenditions(org
* .alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
public List<ChildAssociationRef> getRenditions(NodeRef node, String mimeTypePrefix)
{
List<ChildAssociationRef> allRenditions = this.getRenditions(node);
List<ChildAssociationRef> filteredResults = new ArrayList<ChildAssociationRef>();
for (ChildAssociationRef chAssRef : allRenditions)
{
NodeRef renditionNode = chAssRef.getChildRef();
QName contentProperty = ContentModel.PROP_CONTENT;
Serializable contentPropertyName = nodeService.getProperty(renditionNode,
ContentModel.PROP_CONTENT_PROPERTY_NAME);
if (contentPropertyName != null)
{
contentProperty = (QName) contentPropertyName;
}
ContentReader reader = contentService.getReader(renditionNode, contentProperty);
if (reader != null && reader.exists())
{
String readerMimeType = reader.getMimetype();
if (readerMimeType.startsWith(mimeTypePrefix))
{
filteredResults.add(chAssRef);
}
}
}
return filteredResults;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getRenditionByName(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
public ChildAssociationRef getRenditionByName(NodeRef node, QName renditionName)
{
List<ChildAssociationRef> renditions = new ArrayList<ChildAssociationRef>();
// Check that the node has the renditioned aspect applied
if (nodeService.hasAspect(node, RenditionModel.ASPECT_RENDITIONED) == true)
{
// Get all the renditions that match the given rendition name -
// there should only be 1 (or 0)
renditions = this.nodeService.getChildAssocs(node, RenditionModel.ASSOC_RENDITION, renditionName);
}
if (renditions.isEmpty())
{
return null;
}
else
{
return renditions.get(0);
}
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#isRendition(org.alfresco.service.cmr.repository.NodeRef)
*/
public boolean isRendition(NodeRef node)
{
final QName aspectToCheckFor = RenditionModel.ASPECT_RENDITION;
Set<QName> existingAspects = nodeService.getAspects(node);
for (QName nextAspect : existingAspects)
{
if (nextAspect.equals(aspectToCheckFor) || dictionaryService.isSubClass(nextAspect, aspectToCheckFor))
{
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.rendition.RenditionService#getSourceNode(org.alfresco.service.cmr.repository.NodeRef)
*/
public ChildAssociationRef getSourceNode(NodeRef renditionNode)
{
// In normal circumstances only a node which is itself a rendition can have
// a source node - as linked by the rn:rendition association.
//
// However there are some circumstances where a node which is not
// technically a rendition can still have a source. One such example is the case
// of thumbnail nodes created in a pre-3.3 Alfresco which have not been patched
// to have the correct rendition aspect applied.
// This will also occur *during* execution of the webscript patch and so the
// decision was made not to throw an exception or log a warning if such a
// situation is encountered.
// A rendition node should have 1 and only 1 source node.
List<ChildAssociationRef> parents = nodeService.getParentAssocs(renditionNode,
RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL);
if (parents.size() > 1)
{
StringBuilder msg = new StringBuilder();
msg.append("NodeRef ")
.append(renditionNode)
.append(" unexpectedly has ")
.append(parents.size())
.append(" rendition parents.");
if (log.isWarnEnabled())
{
log.warn(msg.toString());
}
throw new RenditionServiceException(msg.toString());
}
else
{
return parents.isEmpty() ? null : parents.get(0);
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.LinkedList;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.repo.action.ActionDefinitionImpl;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.rendition.RenderingEngineDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
*/
public class RenditionServiceImplTest extends TestCase
{
private final static String ENGINE_NAME = "Engine Name";
private ServiceRegistry serviceRegistry = new MockedTestServiceRegistry();
private ActionService actionService = mock(ActionService.class);
private final RenditionDefinitionPersisterImpl renditionDefinitionPersister = mock(RenditionDefinitionPersisterImpl.class);
private RenditionServiceImpl renditionService;
private final QName ACTION_NAME = QName.createQName(NamespaceService.ALFRESCO_URI, "testName");
@Override
protected void setUp() throws Exception
{
renditionService = new RenditionServiceImpl();
renditionService.setServiceRegistry(serviceRegistry);
renditionService.setActionService(actionService);
renditionService.setRenditionDefinitionPersister(renditionDefinitionPersister);
}
public void testGetRenderingEngineDefinition() throws Exception
{
// Check returns null when unknown name specified.
assertNull(renditionService.getRenderingEngineDefinition(""));
// Check returns null if action service returns an ActionDefinition
// which does not implement RenderingActionDefinition.
ActionDefinition actionDefinition = new ActionDefinitionImpl(ENGINE_NAME);
when(actionService.getActionDefinition(ENGINE_NAME)).thenReturn(actionDefinition);
assertNull(renditionService.getRenderingEngineDefinition(ENGINE_NAME));
// Check returns the definition if the action service returns an
// ActionDefinition
// which does implement RenderingActionDefinition.
ActionDefinition renderingDefinition = new RenderingEngineDefinitionImpl(ENGINE_NAME);
when(actionService.getActionDefinition(ENGINE_NAME)).thenReturn(renderingDefinition);
assertSame(renderingDefinition, renditionService.getRenderingEngineDefinition(ENGINE_NAME));
}
public void testGetRenderingEngineDefinitions() throws Exception
{
LinkedList<ActionDefinition> actionDefs = new LinkedList<ActionDefinition>();
when(actionService.getActionDefinitions()).thenReturn(actionDefs);
// Check case where no action definitions returned.
List<RenderingEngineDefinition> engineDefs = renditionService.getRenderingEngineDefinitions();
assertTrue("The list of rendering action definitions should be empty!", engineDefs.isEmpty());
// Check that when the action service returns a rendering engine
// definition then the rendering service includes this in the list of
// returned values.
ActionDefinition renderingDefinition = new RenderingEngineDefinitionImpl(ENGINE_NAME);
actionDefs.add(renderingDefinition);
engineDefs = renditionService.getRenderingEngineDefinitions();
assertEquals(1, engineDefs.size());
assertSame(renderingDefinition, engineDefs.get(0));
// Check that when the action service returns a non-rendering action
// definition then the rendering service does not include it.
ActionDefinition actionDefinition = new ActionDefinitionImpl(ENGINE_NAME);
actionDefs.add(actionDefinition);
engineDefs = renditionService.getRenderingEngineDefinitions();
assertEquals(1, engineDefs.size());
assertSame(renderingDefinition, engineDefs.get(0));
}
public void testCreateRenditionDefinition() throws Exception
{
RenditionDefinition renderingAction = renditionService.createRenditionDefinition(ACTION_NAME, ENGINE_NAME);
assertNotNull(renderingAction);
assertEquals(ENGINE_NAME, renderingAction.getActionDefinitionName());
assertEquals(ACTION_NAME, renderingAction.getRenditionName());
String id = renderingAction.getId();
assertNotNull(id);
assertTrue(id.length() > 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Neil McErlean
* @since 3.3
*/
public class RenditionServicePermissionsTest extends BaseAlfrescoSpringTest
{
/** Logger */
private static Log logger = LogFactory.getLog(RenditionServicePermissionsTest.class);
private final static QName RESCALE_RENDER_DEFN_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
ImageRenderingEngine.NAME + System.currentTimeMillis());
private NodeRef nodeWithImageContent;
private NodeRef testFolder;
private PermissionService permissionService;
private PersonService personService;
private RenditionService renditionService;
private Repository repositoryHelper;
private RetryingTransactionHelper transactionHelper;
private String testFolderName;
@SuppressWarnings("deprecation")
@Override
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
this.permissionService = (PermissionService)this.applicationContext.getBean("PermissionService");
this.personService = (PersonService)this.applicationContext.getBean("PersonService");
this.renditionService = (RenditionService) this.applicationContext.getBean("renditionService");
this.repositoryHelper = (Repository) this.applicationContext.getBean("repositoryHelper");
this.transactionHelper = (RetryingTransactionHelper) this.applicationContext
.getBean("retryingTransactionHelper");
NodeRef companyHome = this.repositoryHelper.getCompanyHome();
// Create the test folder used for these tests
this.testFolderName= "Test-folder-"+ System.currentTimeMillis();
this.testFolder = nodeService.createNode(companyHome,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.APP_MODEL_1_0_URI,testFolderName),
ContentModel.TYPE_FOLDER).getChildRef();
nodeService.setProperty(testFolder, ContentModel.PROP_NAME, testFolderName);
// Create the node used as a content supplier for tests
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, "Test-image-node-" + System.currentTimeMillis());
String testImageNodeName = "testImageNode" + System.currentTimeMillis();
this.nodeWithImageContent = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.APP_MODEL_1_0_URI, testImageNodeName),
ContentModel.TYPE_CONTENT, props).getChildRef();
// Stream some well-known image content into the node.
URL url = RenditionServicePermissionsTest.class.getClassLoader().getResource("images/gray21.512.png");
assertNotNull("url of test image was null", url);
File imageFile = new File(url.getFile());
assertTrue(imageFile.exists());
nodeService.setProperty(nodeWithImageContent, ContentModel.PROP_CONTENT, new ContentData(null,
MimetypeMap.MIMETYPE_IMAGE_PNG, 0L, null));
ContentWriter writer = contentService.getWriter(nodeWithImageContent, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_IMAGE_PNG);
writer.setEncoding("UTF-8");
writer.putContent(imageFile);
}
@Override
protected void onTearDownInTransaction() throws Exception
{
nodeService.deleteNode(nodeWithImageContent);
nodeService.deleteNode(testFolder);
}
/**
* This test method uses the RenditionService to render a test document and place the
* rendition under a folder for which the user does not have write permissions.
* This should be allowed as all renditions are performed as system.
*/
@SuppressWarnings("deprecation")
public void testRenditionAccessPermissions() throws Exception
{
this.setComplete();
this.endTransaction();
final String normalUser = "renditionUser";
// As admin, create a user who has read-only access to the testFolder
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Set the current security context as admin
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
assertEquals("admin", authenticationService.getCurrentUserName());
if (authenticationService.authenticationExists(normalUser) == false)
{
authenticationService.createAuthentication(normalUser, "PWD".toCharArray());
PropertyMap personProperties = new PropertyMap();
personProperties.put(ContentModel.PROP_USERNAME, normalUser);
personProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + normalUser);
personProperties.put(ContentModel.PROP_FIRSTNAME, "firstName");
personProperties.put(ContentModel.PROP_LASTNAME, "lastName");
personProperties.put(ContentModel.PROP_EMAIL, "email@email.com");
personProperties.put(ContentModel.PROP_JOBTITLE, "jobTitle");
personService.createPerson(personProperties);
}
// Restrict write access to the test folder
permissionService.setPermission(testFolder, normalUser, PermissionService.CONSUMER, true);
return null;
}
});
// As the user, render a piece of content with the rendition going to testFolder
final NodeRef rendition = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
// Set the current security context as a rendition user
AuthenticationUtil.setFullyAuthenticatedUser(normalUser);
assertEquals(normalUser, authenticationService.getCurrentUserName());
assertFalse("Source node has unexpected renditioned aspect.", nodeService.hasAspect(nodeWithImageContent,
RenditionModel.ASPECT_RENDITIONED));
String path = testFolderName+"/testRendition.png";
// Create the rendering action.
RenditionDefinition action = makeRescaleImageAction();
action.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, path);
// Perform the action with an explicit destination folder
logger.debug("Creating rendition of: " + nodeWithImageContent);
ChildAssociationRef renditionAssoc = renditionService.render(nodeWithImageContent, action);
logger.debug("Created rendition: " + renditionAssoc.getChildRef());
NodeRef renditionNode = renditionAssoc.getChildRef();
assertEquals("The parent node was not correct", nodeWithImageContent, renditionAssoc.getParentRef());
logger.debug("rendition's primary parent: " + nodeService.getPrimaryParent(renditionNode));
assertEquals("The parent node was not correct", testFolder, nodeService.getPrimaryParent(renditionNode).getParentRef());
// Now the source content node should have the rn:renditioned aspect
assertTrue("Source node is missing renditioned aspect.", nodeService.hasAspect(nodeWithImageContent,
RenditionModel.ASPECT_RENDITIONED));
return renditionNode;
}
});
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Set the current security context as admin
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
final Serializable renditionCreator = nodeService.getProperty(rendition, ContentModel.PROP_CREATOR);
assertEquals("Incorrect creator", normalUser, renditionCreator);
return null;
}
});
}
/**
* Creates a RenditionDefinition for the RescaleImageActionExecutor.
*
* @return A new RenderingAction.
*/
private RenditionDefinition makeRescaleImageAction()
{
RenditionDefinition result = renditionService.createRenditionDefinition(RESCALE_RENDER_DEFN_NAME,
ImageRenderingEngine.NAME);
result.setParameterValue(ImageRenderingEngine.PARAM_RESIZE_WIDTH, 42);
return result;
}
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.rendition.RenderCallback;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
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.util.EqualsHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Renditioned aspect behaviour bean.
* When any node with the renditioned aspect has a property updated, then all
* associated renditions are eligible for re-rendering.
* Each rendition (as identified by the name in its rn:rendition association) will
* be loaded and if the renditionDefinition exists, the rendition will be updated
* asynchronously, subject to the defined update policy.
*
* @author Neil McErlean
* @author Roy Wetherall
*/
public class RenditionedAspect implements NodeServicePolicies.OnUpdatePropertiesPolicy,
CopyServicePolicies.OnCopyNodePolicy
{
/** logger */
private static final Log logger = LogFactory.getLog(RenditionedAspect.class);
/** Services */
private PolicyComponent policyComponent;
private NodeService nodeService;
private DictionaryService dictionaryService;
private RenditionService renditionService;
/**
* Set the policy component
*
* @param policyComponent policy component
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Set the node service
*
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the rendition service
*
* @param renditionService rendition service
*/
public void setRenditionService(RenditionService renditionService)
{
this.renditionService = renditionService;
}
/**
* Set the dictionary service
*
* @param dictionaryService dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* Initialise method
*/
public void init()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
RenditionModel.ASPECT_RENDITIONED,
new JavaBehaviour(this, "onUpdateProperties", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
RenditionModel.ASPECT_RENDITIONED,
new JavaBehaviour(this, "getCopyCallback"));
}
/**
* @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<QName, Serializable> before,
Map<QName, Serializable> after)
{
// Ignore working copies
if (this.nodeService.exists(nodeRef) == true &&
this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == false)
{
// Find the changed properties
List<QName> changedProperties = getChangedProperties(before, after);
// There may be a different policy for different rendition kinds.
List<ChildAssociationRef> renditions = this.renditionService.getRenditions(nodeRef);
for (ChildAssociationRef chAssRef : renditions)
{
QName renditionAssocName = chAssRef.getQName();
RenditionDefinition rendDefn = this.renditionService.loadRenditionDefinition(renditionAssocName);
if (rendDefn == null)
{
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Cannot update rendition ")
.append(renditionAssocName)
.append(" on node ").append(nodeRef)
.append(" as the renditionDefinition could not be loaded.");
logger.debug(msg.toString());
}
continue;
}
Serializable updateRenditionsPolicy = rendDefn.getParameterValue(AbstractRenderingEngine.PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE);
boolean updateRenditionsAlways = updateRenditionsPolicy == null ? false : (Boolean)updateRenditionsPolicy;
boolean renditionUpdateRequired = false;
for (QName qname : changedProperties)
{
try
{
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
if (propertyDef == null)
{
// the property is not recognised
continue;
}
else if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
{
// not a content type
if (updateRenditionsAlways)
{
renditionUpdateRequired = true;
}
continue;
}
else
{
// it is a content property. We always update renditions for changes to content.
renditionUpdateRequired = true;
}
} catch (ClassCastException ccx)
{
// the property does not confirm to the model
continue;
}
}
if (renditionUpdateRequired)
{
this.queueUpdate(nodeRef, rendDefn, chAssRef);
}
}
}
}
private List<QName> getChangedProperties(Map<QName, Serializable> before, Map<QName, Serializable> after)
{
List<QName> results = new ArrayList<QName>();
for (QName propQName : before.keySet())
{
if (after.keySet().contains(propQName) == false)
{
// property was deleted
results.add(propQName);
}
else
{
Serializable beforeValue = before.get(propQName);
Serializable afterValue = after.get(propQName);
if (EqualsHelper.nullSafeEquals(beforeValue, afterValue) == false)
{
// Property was changed
results.add(propQName);
}
}
}
for (QName propQName : after.keySet())
{
if (before.containsKey(propQName) == false)
{
// property was added
results.add(propQName);
}
}
return results;
}
/**
* Queue the update to happen asynchronously
*
* @param sourceNodeRef node reference
*/
private void queueUpdate(final NodeRef sourceNodeRef, final RenditionDefinition rendDefn,
final ChildAssociationRef renditionAssoc)
{
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Queueing rendition update for node ").append(sourceNodeRef).append(": ").append(rendDefn.getRenditionName());
logger.debug(msg.toString());
}
if (rendDefn != null)
{
renditionService.render(sourceNodeRef, rendDefn, new RenderCallback()
{
public void handleFailedRendition(Throwable t)
{
// In the event of a failed re-rendition, we will delete the rendition node
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Re-rendering of node ")
.append(sourceNodeRef)
.append(" with renditionDefinition ")
.append(rendDefn.getRenditionName())
.append(" failed. Deleting defunct rendition. ")
.append("The following exception is shown for informational purposes only ")
.append("and does not affect operation of the system.");
logger.debug(msg.toString(), t);
}
if (nodeService.exists(renditionAssoc.getChildRef()))
{
nodeService.deleteNode(renditionAssoc.getChildRef());
}
}
public void handleSuccessfulRendition(ChildAssociationRef primaryParentOfNewRendition)
{
// Intentionally empty
}
});
}
}
/**
* @return Returns {@link RenditionedAspectCopyBehaviourCallback}
*/
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
{
return RenditionedAspectCopyBehaviourCallback.INSTANCE;
}
/**
* Behaviour for the {@link RenditionModel#ASPECT_RENDITIONED <b>rn:renditioned</b>} aspect.
*
* @author Derek Hulley
* @author Neil McErlean
* @since 3.2
*/
private static class RenditionedAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
{
private static final CopyBehaviourCallback INSTANCE = new RenditionedAspectCopyBehaviourCallback();
/**
* @return Returns <tt>true</tt> always
*/
@Override
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
{
return true;
}
/**
* Copy rendition-related associations, {@link RenditionModel#ASSOC_RENDITION} regardless of
* cascade options.
*/
@Override
public ChildAssocCopyAction getChildAssociationCopyAction(
QName classQName,
CopyDetails copyDetails,
CopyChildAssociationDetails childAssocCopyDetails)
{
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
if (childAssocRef.getTypeQName().equals(RenditionModel.ASSOC_RENDITION))
{
return ChildAssocCopyAction.COPY_CHILD;
}
else
{
throw new IllegalStateException(
"Behaviour should have been invoked: \n" +
" Aspect: " + this.getClass().getName() + "\n" +
" " + childAssocCopyDetails + "\n" +
" " + copyDetails);
}
}
/**
* Copy only the {@link ContentModel#PROP_AUTOMATIC_UPDATE}
*/
@Override
public Map<QName, Serializable> getCopyProperties(
QName classQName,
CopyDetails copyDetails,
Map<QName, Serializable> properties)
{
Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>(5);
// Serializable value = properties.get(ContentModel.PROP_AUTOMATIC_UPDATE);
// newProperties.put(ContentModel.PROP_AUTOMATIC_UPDATE, value);
return newProperties;
}
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.util.FreeMarkerUtil;
import org.alfresco.util.XMLUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import freemarker.ext.dom.NodeModel;
import freemarker.template.SimpleDate;
import freemarker.template.SimpleHash;
public class StandardRenditionLocationResolverImpl implements RenditionLocationResolver
{
private final static Log log = LogFactory.getLog(StandardRenditionLocationResolverImpl.class);
private ServiceRegistry serviceRegistry;
private String companyHomePath = "/app:company_home";
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public void setCompanyHomePath(String companyHomePath)
{
this.companyHomePath = companyHomePath;
}
/*
* (non-Javadoc)
*
* @seeorg.alfresco.repo.rendition.RenditionLocationResolver#
* resolveRenditionPrimaryParentAssoc
* (org.alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.cmr.rendition.RenditionDefinition,
* org.alfresco.service.cmr.repository.ChildAssociationRef)
*/
public RenditionLocation getRenditionLocation(NodeRef sourceNode, RenditionDefinition definition, NodeRef tempRenditionLocation)
{
// If a destination NodeRef is specified then don't botther to find the location as one has already been specified.
NodeRef destination = AbstractRenderingEngine.getCheckedParam(RenditionService.PARAM_DESTINATION_NODE, NodeRef.class, definition);
if(destination!=null)
{
RenditionLocationImpl location = createNodeLocation(destination);
return location;
}
// If the templated path has been specified and can be resolved then
// find or create the templated path location and return it.
String pathTemplate = (String) definition.getParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE);
if (pathTemplate != null)
{
NodeRef companyHome = getCompanyHomeNode(sourceNode.getStoreRef());
NodeService nodeService = serviceRegistry.getNodeService();
Serializable companyHomeName = nodeService.getProperty(companyHome, ContentModel.PROP_NAME);
String path = renderPathTemplate(pathTemplate, sourceNode, tempRenditionLocation, companyHomeName);
if(path!=null)
{
return findOrCreateTemplatedPath(sourceNode, path, companyHome);
}
}
// Otherwise the rendition will be created as a child of the source content node.
return new RenditionLocationImpl(sourceNode, null, null);
}
private RenditionLocationImpl createNodeLocation(NodeRef destination)
{
NodeService nodeService = serviceRegistry.getNodeService();
if(nodeService.exists(destination)==false)
throw new RenditionServiceException("The rendition destination node does not exist! NodeRef: "+destination);
NodeRef parentRef = nodeService.getPrimaryParent(destination).getParentRef();
String childName = (String) nodeService.getProperty(destination, ContentModel.PROP_NAME);
RenditionLocationImpl location = new RenditionLocationImpl(parentRef, destination, childName);
return location;
}
private RenditionLocationImpl findOrCreateTemplatedPath(NodeRef sourceNode, String path, NodeRef companyHome)
{
NodeService nodeService = serviceRegistry.getNodeService();
List<String> pathElements = Arrays.asList(path.split("/"));
LinkedList<String> folderElements = new LinkedList<String>(pathElements);
// Remove empty folder caused by path starting with / .
if(folderElements.getFirst().length() == 0)
{
folderElements.removeFirst();
}
// Remove 'Company Home' if it is at the start of the path.
Serializable companyHomeName = nodeService.getProperty(companyHome, ContentModel.PROP_NAME);
if(folderElements.getFirst().equals(companyHomeName))
{
folderElements.removeFirst();
}
String fileName = folderElements.removeLast();
if (fileName == null || fileName.length() == 0)
{
throw new RenditionServiceException("The path must include a valid filename! Path: " + path);
}
FileFolderService fileFolderService = serviceRegistry.getFileFolderService();
FileInfo parentInfo = FileFolderServiceImpl.makeFolders(fileFolderService,
companyHome, folderElements,
ContentModel.TYPE_FOLDER);
NodeRef parent = parentInfo.getNodeRef();
NodeRef child = fileFolderService.searchSimple(parent, fileName);
return new RenditionLocationImpl(parent, child, fileName);
}
private String renderPathTemplate(String pathTemplate, NodeRef sourceNode, NodeRef tempRenditionLocation, Serializable companyHomeName)
{
NodeService nodeService = serviceRegistry.getNodeService();
NamespaceService namespaceService = serviceRegistry.getNamespaceService();
final Map<String, Object> root = new HashMap<String, Object>();
ChildAssociationRef sourceAssoc = nodeService.getPrimaryParent(sourceNode);
String fullSourceName = sourceAssoc.getQName().getLocalName();
String trimmedSourceName = fullSourceName;
String sourceExtension = "";
int extensionIndex = fullSourceName.lastIndexOf('.');
if (extensionIndex != -1)
{
trimmedSourceName = (extensionIndex == 0) ? "" : fullSourceName.substring(0, extensionIndex);
sourceExtension = (extensionIndex == fullSourceName.length() - 1) ? "" : fullSourceName
.substring(extensionIndex + 1);
}
Path sourcePath = nodeService.getPath(sourceNode);
StoreRef store = sourceNode.getStoreRef();
getCompanyHomeNode( store);
root.put("name", trimmedSourceName);
root.put("extension", sourceExtension);
root.put("date", new SimpleDate(new Date(), SimpleDate.DATETIME));
root.put("cwd", sourcePath.toPrefixString(namespaceService));
root.put("companyHome", companyHomeName);
root.put("sourceNode", new TemplateNode(sourceNode, serviceRegistry, null));
root.put("sourceContentType", nodeService.getType(sourceNode).getLocalName());
root.put("renditionContentType", nodeService.getType(tempRenditionLocation).getLocalName());
NodeRef person = serviceRegistry.getPersonService().getPerson(AuthenticationUtil.getFullyAuthenticatedUser());
root.put("person", new TemplateNode(person, serviceRegistry, null));
if (sourceNodeIsXml(sourceNode))
{
try
{
Document xml = XMLUtil.parse(sourceNode, serviceRegistry.getContentService());
pathTemplate = FreeMarkerUtil.buildNamespaceDeclaration(xml) + pathTemplate;
root.put("xml", NodeModel.wrap(xml));
} catch (Exception ex)
{
log.warn("Failed to parse XML content into path template model: Node = " + sourceNode);
}
}
if (log.isDebugEnabled())
{
log.debug("Path template model: " + root);
}
String result = null;
try
{
if (log.isDebugEnabled())
{
log.debug("Processing " + pathTemplate + " using source node " + sourcePath);
}
result = serviceRegistry.getTemplateService().processTemplateString("freemarker", pathTemplate,
new SimpleHash(root));
} catch (TemplateException te)
{
log.error("Error while trying to process rendition path template: " + pathTemplate);
log.error(te.getMessage(), te);
}
if (log.isDebugEnabled())
{
log.debug("processed pattern " + pathTemplate + " as " + result);
}
return result;
}
private NodeRef getCompanyHomeNode(StoreRef store)
{
SearchService searchService = serviceRegistry.getSearchService();
ResultSet result = searchService.query(store, SearchService.LANGUAGE_XPATH, companyHomePath);
if(result.length()==0)
return null;
else
return result.getNodeRef(0);
}
protected boolean sourceNodeIsXml(NodeRef sourceNode)
{
boolean result = false;
// TODO: BJR 20100211: We can do better than this...
ContentReader reader = serviceRegistry.getContentService().getReader(sourceNode, ContentModel.PROP_CONTENT);
if ((reader != null) && reader.exists())
{
result = (reader.getContentData().getMimetype().equals("text/xml"));
}
return result;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.GUID;
/**
* @author Brian Remmington
* @author Nick Smith
*/
public class StandardRenditionLocationResolverTest extends BaseAlfrescoSpringTest
{
private ServiceRegistry serviceRegistry;
private NodeRef companyHome;
private StandardRenditionLocationResolverImpl locationResolver;
private RenditionService renditionService;
private SearchService searchService;
/**
* Called during the transaction setup
*/
@Override
@SuppressWarnings("deprecation")
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
// Get the required services
this.serviceRegistry = (ServiceRegistry) this.getApplicationContext().getBean("ServiceRegistry");
this.nodeService=serviceRegistry.getNodeService();
this.searchService = serviceRegistry.getSearchService();
this.renditionService = (RenditionService) this.getApplicationContext().getBean("RenditionService");
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH,
"/app:company_home");
if (rs.length() != 1)
{
fail("Could not find company home");
}
companyHome = rs.getNodeRef(0);
locationResolver = new StandardRenditionLocationResolverImpl();
locationResolver.setServiceRegistry(serviceRegistry);
}
public void testChildAssociationFinder()
{
QName renditionKind = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "test");
NodeRef sourceNode = makeNode(companyHome, ContentModel.TYPE_CONTENT);
NodeRef tempRenditionNode = makeNode(sourceNode, ContentModel.TYPE_CONTENT);
RenditionDefinition renditionDef = renditionService.createRenditionDefinition(renditionKind,
"brians_test_engine");
RenditionLocation location =
locationResolver.getRenditionLocation(sourceNode,renditionDef, tempRenditionNode);
assertEquals(sourceNode, location.getParentRef());
assertNull(location.getChildName());
assertNull(location.getChildRef());
NodeRef targetFolder = makeNode(companyHome, ContentModel.TYPE_FOLDER);
String companyHomeName = (String) nodeService.getProperty(companyHome, ContentModel.PROP_NAME);
String targetFolderName = (String) nodeService.getProperty(targetFolder, ContentModel.PROP_NAME);
String template = "/"+companyHomeName+"/"+targetFolderName+"/brian.xml";
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, template);
location = locationResolver.getRenditionLocation(sourceNode, renditionDef, tempRenditionNode);
assertEquals(targetFolder, location.getParentRef());
assertEquals("brian.xml", location.getChildName());
assertNull(location.getChildRef());
template=targetFolderName+"/test-${sourceContentType}.xml";
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, template);
location = locationResolver.getRenditionLocation(sourceNode, renditionDef, tempRenditionNode);
assertEquals(targetFolder, location.getParentRef());
assertEquals("test-"+ContentModel.PROP_CONTENT.getLocalName()+".xml", location.getChildName());
assertNull(location.getChildRef());
// Test that when the template path specifies an existing node then that
// node is set as the 'childRef' property on the RenditionLocation.
NodeRef destinationNode = makeNode(targetFolder, ContentModel.TYPE_CONTENT);
String destinationName = (String) nodeService.getProperty(destinationNode, ContentModel.PROP_NAME);
template = targetFolderName + "/" + destinationName;
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, template);
location = locationResolver.getRenditionLocation(sourceNode, renditionDef, tempRenditionNode);
assertEquals(targetFolder, location.getParentRef());
assertEquals(destinationName, location.getChildName());
assertEquals(destinationNode, location.getChildRef());
// Test that the 'destination node' param takes precedence over them 'template path' param.
template = "/" + targetFolderName + "/brian.xml";
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, template);
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_NODE, destinationNode);
location = locationResolver.getRenditionLocation(sourceNode, renditionDef, tempRenditionNode);
assertEquals(targetFolder, location.getParentRef());
assertEquals(destinationName, location.getChildName());
assertEquals(destinationNode, location.getChildRef());
}
public void testCreatesFoldersForTemplatedLocation() throws Exception
{
QName fooName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFooFolder");
QName barName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testBarFolder");
String fooPath = "/testFooFolder";
String barPath = fooPath + "/testBarFolder";
List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(companyHome, ContentModel.ASSOC_CONTAINS,
fooName);
assertTrue("Folder " + fooPath + " should not exist!", childAssocs.isEmpty());
QName renditionKind = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "test");
NodeRef sourceNode = makeNode(companyHome, ContentModel.TYPE_CONTENT);
NodeRef tempRenditionNode = makeNode(companyHome, ContentModel.TYPE_CONTENT);
RenditionDefinition renditionDef = renditionService.createRenditionDefinition(renditionKind,
"nicks_test_engine");
String pathTemplate = barPath + "/nick.xml";
renditionDef.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, pathTemplate);
RenditionLocation location =
locationResolver.getRenditionLocation(sourceNode,renditionDef, tempRenditionNode);
NodeRef fooNode = checkFolder(fooName, companyHome, "Foo");
NodeRef barNode = checkFolder(barName, fooNode, "Bar");
assertEquals("Bar is not the rendition parent!", barNode, location.getParentRef());
assertEquals("nick.xml", location.getChildName());
}
// public void testOldRenditionIsOrphaned() throws Exception
// {
// QName renditionKind = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "test");
// NodeRef sourceNode = makeNode(companyHome, ContentModel.TYPE_CONTENT);
// NodeRef tempRenditionNode = makeNode(sourceNode, ContentModel.TYPE_CONTENT);
// ChildAssociationRef tempRenditionLocation=nodeService.getPrimaryParent(tempRenditionNode);
//
// RenditionDefinition definition = renditionService.createRenditionDefinition(renditionKind,
// "nicks_test_engine");
// ChildAssociationRef oldRendition =
// nodeService.createNode(sourceNode, RenditionModel.ASSOC_RENDITION, definition.getRenditionName(), ContentModel.TYPE_CONTENT);
// definition.setParameterValue(RenditionService.PARAM_ORPHAN_EXISTING_RENDITION, true);
//
// //Test deletes old rendition if it's under source Node and new rendition is not.
// locationResolver.getRenditionLocation(sourceNode, definition, tempRenditionLocation);
//
// List<ChildAssociationRef> sourceChildren = nodeService.getChildAssocs(sourceNode);
// assertFalse("The old rendition association should ahve been removed!", sourceChildren.contains(oldRendition));
// assertFalse("The old rendition should have been deleted!", nodeService.exists(oldRendition.getChildRef()) );
// }
private NodeRef checkFolder(QName folderName, NodeRef parentName, String folderMessageName)
{
List<ChildAssociationRef> folderAssocs = nodeService.getChildAssocs(parentName, ContentModel.ASSOC_CONTAINS, folderName);
assertEquals("Folder " + folderMessageName + " should exist!", 1, folderAssocs.size());
NodeRef folderNode = folderAssocs.get(0).getChildRef();
assertEquals(folderMessageName+" node is wrong type!", ContentModel.TYPE_FOLDER, nodeService.getType(folderNode));
assertEquals(folderMessageName+" node has wrong name!", folderName.getLocalName(),
nodeService.getProperty(folderNode, ContentModel.PROP_NAME));
return folderNode;
}
private NodeRef makeNode(NodeRef parent, QName nodeType)
{
String uuid = GUID.generate();
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, uuid);
ChildAssociationRef assoc = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, QName.createQName(
NamespaceService.APP_MODEL_1_0_URI, uuid), nodeType, props);
return assoc.getChildRef();
}
}

View File

@@ -0,0 +1,553 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import static org.alfresco.service.cmr.rendition.RenditionService.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.rendition.RenderingEngineDefinitionImpl;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class adds some new behaviour to the standard ActionExecuterAbstractBase
* in order to support the RenditionService.
*
* @author Neil McErlean
* @author Nick Smith
* @since 3.3
*/
public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase
{
/** Logger */
private static Log logger = LogFactory.getLog(AbstractRenderingEngine.class);
protected static final String CONTENT_READER_NOT_FOUND_MESSAGE = "Cannot find Content Reader for document. Operation can't be performed";
// A word on the default* fields below:
//
// RenditionExecuters can be executed with or without two optional
// parameters: "rendition node type"
// and a "rendition content property" parameter.
// These parameters can be specified on a per-action basis.
// If no value is specified, then the default is used.
// That default can be injected via Spring.
// If no default is injected via spring, then there is a "default default"
// for the two params
/**
* This is the default default node type for renditions - used if no value
* is injected from spring.
*/
private static final QName DEFAULT_DEFAULT_RENDITION_NODE_TYPE = ContentModel.TYPE_CONTENT;
/**
* This is the default default property used to specify where rendition
* content is stored - used if no value is injected from spring.
*/
private static final QName DEFAULT_DEFAULT_RENDITION_CONTENT_PROP = ContentModel.PROP_CONTENT;
private static final String DEFAULT_MIMETYPE = MimetypeMap.MIMETYPE_TEXT_PLAIN;
private static final String DEFAULT_ENCODING = "UTF-8";
/**
* This is the default node type that is used when creating rendition
* objects.
*/
private QName defaultRenditionNodeType = DEFAULT_DEFAULT_RENDITION_NODE_TYPE;
/**
* This is the default property that is used to store rendition objects'
* content.
*/
private QName defaultRenditionContentProp = DEFAULT_DEFAULT_RENDITION_CONTENT_PROP;
/**
* This is the default content property.
*/
private static final QName DEFAULT_CONTENT_PROPERTY = ContentModel.TYPE_CONTENT;
/* Injected Services */
protected ContentService contentService;
protected MimetypeMap mimetypeMap;
protected NodeService nodeService;
/* Parameter names common to all Rendering Actions */
//TODO javadoc these
public static final String PARAM_PLACEHOLDER_RESOURCE_PATH = "placeHolderResourcePath";
public static final String PARAM_SOURCE_CONTENT_PROPERTY = "sourceContentProperty";
public static final String PARAM_TARGET_CONTENT_PROPERTY = "targetContentProperty";
public static final String PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE = "update-renditions-on-any-property-change";
public static final String PARAM_RUN_AS = "runAs";
/*
* mime-type is not a common parameter on all Rendering Actions, but it is
* common to many and is used in some common handling code in this class.
*/
public static final String PARAM_MIME_TYPE = "mime-type";
public static final String PARAM_ENCODING = "encoding";
/**
* Sets the default rendition-node type.
*
* @param type
*/
public void setDefaultRenditionNodeType(String type)
{
QName qname;
try
{
qname = QName.createQName(type);
}
catch (NamespaceException nx)
{
if (logger.isErrorEnabled())
{
logger.error("Error when setting default rendition node type: ", nx);
}
throw nx;
}
if (logger.isInfoEnabled())
{
logger.info("Using default rendition node type: " + qname);
}
this.defaultRenditionNodeType = qname;
}
/**
* This method returns the type of the default rendition node type.
*
* @return the QName representing the type of the default rendition node
* type.
*/
protected QName getDefaultRenditionNodeType()
{
return defaultRenditionNodeType;
}
protected String getTargetMimeType(RenderingContext context)
{
return context.getParamWithDefault(PARAM_MIME_TYPE, DEFAULT_MIMETYPE);
}
protected String getTargetEncoding(RenderingContext context)
{
return context.getParamWithDefault(PARAM_ENCODING, DEFAULT_ENCODING);
}
/**
* Sets the default rendition content property.
*
* @param prop
*/
public void setDefaultRenditionContentProp(String prop)
{
QName qname;
try
{
qname = QName.createQName(prop);
}
catch (NamespaceException nx)
{
if (logger.isErrorEnabled())
{
logger.error("Error when setting default rendition content property: ", nx);
}
throw nx;
}
if (logger.isInfoEnabled())
{
logger.info("Using default rendition content property: " + qname);
}
this.defaultRenditionContentProp = qname;
}
/**
* This method returns the QName of the property that defines the location
* of the rendition content. An example would be cm:content.
*
* @return the QName the property defining the location of the rendition
* content.
*/
protected QName getDefaultRenditionContentProp()
{
return defaultRenditionContentProp;
}
/**
* Set the content service
*
* @param contentService the content service
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* This method sets the nodeService.
*
* @param nodeService the namespaceService
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setMimetypeMap(MimetypeMap mimetypeMap)
{
this.mimetypeMap = mimetypeMap;
}
@Override
protected ActionDefinition createActionDefinition(String definitionName)
{
return new RenderingEngineDefinitionImpl(definitionName);
}
@Override
protected void executeImpl(Action action, NodeRef sourceNode)
{
checkParameterValues(action);
checkActionIsRenditionDefinition(action);
checkSourceNodeExists(sourceNode);
RenditionDefinition renditionDefinition = (RenditionDefinition) action;
ChildAssociationRef renditionAssoc = createRenditionNodeAssoc(sourceNode, renditionDefinition);
QName targetContentProp = getRenditionContentProperty(renditionDefinition);
NodeRef destinationNode = renditionAssoc.getChildRef();
RenderingContext context = new RenderingContext(sourceNode,//
destinationNode,//
renditionDefinition,//
targetContentProp);
render(context);
// This is a workaround for the fact that actions don't have return
// values.
action.getParameterValues().put(PARAM_RESULT, renditionAssoc);
}
/**
* This method can be overridden by subclasses to provide checking of parameter
* values.
* If a parameter value is illegal or inappropriate, an exception
* should be thrown.
*/
protected void checkParameterValues(Action action)
{
// Intentionally empty
}
/**
* @param renditionDefinition
* @return
*/
protected QName getRenditionContentProperty(RenditionDefinition renditionDefinition)
{
return getParamWithDefault(PARAM_TARGET_CONTENT_PROPERTY, defaultRenditionContentProp, renditionDefinition);
}
protected abstract void render(RenderingContext context);
/**
* @param actionedUponNodeRef
*/
protected void checkSourceNodeExists(NodeRef actionedUponNodeRef)
{
if (nodeService.exists(actionedUponNodeRef) == false)
{
String msg = "Cannot execute action as node does not exist: " + actionedUponNodeRef;
logger.warn(msg);
throw new RenditionServiceException(msg);
}
}
/**
* @param action
*/
protected void checkActionIsRenditionDefinition(Action action)
{
if (action instanceof RenditionDefinition == false)
{
String msg = "Cannot execute action as it is not a RenditionDefinition: " + action;
logger.warn(msg);
throw new RenditionServiceException(msg);
}
}
/**
* If no rendition node type is specified, then the default is used
*
* @param renditionDefinition
* @return
*/
public QName getRenditionNodeType(RenditionDefinition renditionDefinition)
{
return getParamWithDefault(PARAM_RENDITION_NODETYPE, defaultRenditionNodeType, renditionDefinition);
}
@Override
final protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.addAll(getParameterDefinitions());
}
/**
* Supplies the list of parameters required by this rendering engine.
*
* @return
*/
protected Collection<ParameterDefinition> getParameterDefinitions()
{
List<ParameterDefinition> paramList = new ArrayList<ParameterDefinition>();
paramList.add(new ParameterDefinitionImpl(PARAM_RUN_AS, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_RUN_AS)));
paramList.add(new ParameterDefinitionImpl(PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_UPDATE_RENDITIONS_ON_ANY_PROPERTY_CHANGE)));
paramList.add(new ParameterDefinitionImpl(PARAM_RENDITION_NODETYPE, DataTypeDefinition.QNAME, false,
getParamDisplayLabel(PARAM_RENDITION_NODETYPE)));
paramList.add(new ParameterDefinitionImpl(PARAM_PLACEHOLDER_RESOURCE_PATH, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_PLACEHOLDER_RESOURCE_PATH)));
paramList.add(new ParameterDefinitionImpl(PARAM_SOURCE_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false,
getParamDisplayLabel(PARAM_SOURCE_CONTENT_PROPERTY)));
paramList.add(new ParameterDefinitionImpl(PARAM_TARGET_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false,
getParamDisplayLabel(PARAM_TARGET_CONTENT_PROPERTY)));
paramList.add(new ParameterDefinitionImpl(PARAM_DESTINATION_PATH_TEMPLATE, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_DESTINATION_PATH_TEMPLATE)));
paramList.add(new ParameterDefinitionImpl(PARAM_ORPHAN_EXISTING_RENDITION, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_ORPHAN_EXISTING_RENDITION)));
return paramList;
}
private ChildAssociationRef createRenditionNodeAssoc(NodeRef sourceNode, RenditionDefinition renditionDefinition)
{
QName renditionName = renditionDefinition.getRenditionName();
// The ThumbnailService puts a cm:name property on its thumbnail nodes.
Map<QName, Serializable> nodeProps = new HashMap<QName, Serializable>();
nodeProps.put(ContentModel.PROP_NAME, renditionName.getLocalName());
nodeProps.put(ContentModel.PROP_CONTENT_PROPERTY_NAME, getDefaultRenditionContentProp());
QName assocName = QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, GUID.generate());
NodeRef parentNode = renditionDefinition.getRenditionParent();
QName assocType = renditionDefinition.getRenditionAssociationType();
QName nodeType = getRenditionNodeType(renditionDefinition);
ChildAssociationRef childAssoc = nodeService.createNode(parentNode, assocType, assocName, nodeType, nodeProps);
return childAssoc;
}
/**
* Gets the value for the named parameter. Checks the type of the parameter
* is correct and throws a {@link RenditionServiceException} if it isn't.
* Returns <code>null</code> if the parameter value is <code>null</code>
*
* @param paramName the name of the parameter being checked.
* @param clazz the expected {@link Class} of the parameter value.
* @param definition the {@link RenditionDefinition} containing the
* parameters.
* @return the parameter value or <code>null</code>.
*/
@SuppressWarnings("unchecked")
public static <T> T getCheckedParam(String paramName, Class<T> clazz, RenditionDefinition definition)
{
Serializable value = definition.getParameterValue(paramName);
if (value == null)
return null;
else
{
Class<? extends Serializable> valueClass = value.getClass();
if (!valueClass.isAssignableFrom(clazz))
{
throw new RenditionServiceException("The parameter: " + paramName + " must be of type: "
+ clazz.getName() + "but was of type: " + valueClass.getName());
}
else
return (T) value;
}
}
/**
* Gets the value for the named parameter. Checks the type of the parameter
* is the same as the type of <code>defaultValue</code> and throws a
* {@link RenditionServiceException} if it isn't. Returns
* <code>defaultValue</code> if the parameter value is <code>null</code>
*
* @param <T>
* @param paramName
* @param defaultValue
* @param definition
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getParamWithDefault(String paramName, T defaultValue, RenditionDefinition definition)
{
Class<? extends T> clazz = (Class<? extends T>) defaultValue.getClass();
T result = getCheckedParam(paramName, clazz, definition);
if (result == null)
result = defaultValue;
return result;
}
protected class RenderingContext
{
private final NodeRef sourceNode;
private final NodeRef destinationNode;
private final RenditionDefinition definition;
private final QName renditionContentProperty;
/**
* @param sourceNode
* @param destinationNode
* @param definition
* @param renditionContentProperty
*/
public RenderingContext(NodeRef sourceNode,//
NodeRef destinationNode,//
RenditionDefinition definition,//
QName renditionContentProperty)
{
this.sourceNode = sourceNode;
this.destinationNode = destinationNode;
this.definition = definition;
this.renditionContentProperty = renditionContentProperty;
}
/**
* @return the sourceNode
*/
public NodeRef getSourceNode()
{
return this.sourceNode;
}
/**
* @return the destinationNode
*/
public NodeRef getDestinationNode()
{
return this.destinationNode;
}
/**
* @return the definition
*/
public RenditionDefinition getDefinition()
{
return this.definition;
}
/**
* Gets the value for the named parameter from the . Checks the type of
* the parameter is correct and throws and Exception if it isn't.
* Returns <code>null</code> if the parameter value is <code>null</code>
*
* @param paramName the name of the parameter being checked.
* @param clazz the expected {@link Class} of the parameter value.
* @return the parameter value or <code>null</code>.
*/
public <T> T getCheckedParam(String paramName, Class<T> clazz)
{
return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition);
}
/**
* Gets the value for the named parameter. Checks the type of the
* parameter is the same as the type of <code>defaultValue</code> and
* throws a {@link RenditionServiceException} if it isn't. Returns
* <code>defaultValue</code> if the parameter value is <code>null</code>
*
* @param <T>
* @param paramName
* @param defaultValue
* @return
*/
public <T> T getParamWithDefault(String paramName, T defaultValue)
{
return AbstractRenderingEngine.getParamWithDefault(paramName, defaultValue, definition);
}
public ContentReader makeContentReader()
{
QName srcContentProp = getParamWithDefault(PARAM_SOURCE_CONTENT_PROPERTY, DEFAULT_CONTENT_PROPERTY);
ContentReader contentReader = contentService.getReader(sourceNode, srcContentProp);
if (contentReader == null || !contentReader.exists())
{
throw new RenditionServiceException(CONTENT_READER_NOT_FOUND_MESSAGE);
}
return contentReader;
}
public ContentWriter makeContentWriter()
{
ContentWriter contentWriter = contentService.getWriter(destinationNode, renditionContentProperty, true);
String mimetype = getTargetMimeType(this);
contentWriter.setMimetype(mimetype);
String encoding = getTargetEncoding(this);
contentWriter.setEncoding(encoding);
return contentWriter;
}
public int getIntegerParam(String key, int defaultValue)
{
Serializable serializable = definition.getParameterValue(key);
if (serializable == null)
return defaultValue;
else
{
Number number = (Number) serializable;
return number.intValue();
}
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import org.alfresco.repo.content.transform.ContentTransformer;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NoTransformerException;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Nick Smith
*/
public abstract class AbstractTransformationRenderingEngine extends AbstractRenderingEngine
{
private static Log logger = LogFactory.getLog(AbstractTransformationRenderingEngine.class);
/* Error messages */
private static final String TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN = "Transformer for '%s' source mime type and '%s' target mime type was not found. Operation can't be performed";
private static final String NOT_TRANSFORMABLE_MESSAGE_PATTERN = "Content not transformable for '%s' source mime type and '%s' target mime type. Operation can't be performed";
private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: ";
/*
* (non-Javadoc)
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext)
*/
@Override
protected void render(RenderingContext context)
{
ContentReader contentReader = context.makeContentReader();
String sourceMimeType = contentReader.getMimetype();
String targetMimeType = getTargetMimeType(context);
TransformationOptions options = getTransformOptions(context);
ContentTransformer transformer = this.contentService.getTransformer(sourceMimeType, targetMimeType, options);
// Actually perform the rendition.
if (null == transformer)
{
throw new RenditionServiceException(String.format(TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN, sourceMimeType,
targetMimeType));
}
if (transformer.isTransformable(sourceMimeType, targetMimeType, options))
{
ContentWriter contentWriter = context.makeContentWriter();
try
{
contentService.transform(contentReader, contentWriter, options);
}
catch (NoTransformerException ntx)
{
{
logger.debug("No transformer found to execute rule: \n" + " reader: " + contentReader + "\n"
+ " writer: " + contentWriter + "\n" + " action: " + this);
}
throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + ntx.getMessage(), ntx);
}
}
else
{
throw new RenditionServiceException(String.format(NOT_TRANSFORMABLE_MESSAGE_PATTERN, sourceMimeType, targetMimeType));
}
}
protected abstract TransformationOptions getTransformOptions(RenderingContext context);
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This RenderingEngine is used for rendering
* {@link CompositeRenditionDefinition}s, which specify a list of
* {@link RenditionDefinition}s. The {@link CompositeRenderingEngine} iterates
* over the {@link RenditionDefinition}s sequentially and feeds the output of
* one definition in as the input of the next definition. The output of the last
* definition executed is the output of this rendering engine.
*
* @author Nick Smith
*/
public class CompositeRenderingEngine extends AbstractRenderingEngine
{
/** Logger */
private static Log logger = LogFactory.getLog(CompositeRenderingEngine.class);
public static final String NAME = "compositeRenderingEngine";
private ActionService actionService;
/*
* @see
* org.alfresco.repo.rendition.executer.AbstractRenderingEngine#executeImpl
* (org.alfresco.service.cmr.action.Action,
* org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected void executeImpl(Action action, NodeRef sourceNode)
{
checkSourceNodeExists(sourceNode);
if (action instanceof CompositeRenditionDefinition)
{
CompositeRenditionDefinition compositeDefinition = (CompositeRenditionDefinition) action;
ChildAssociationRef renditionAssoc = executeCompositeRendition(compositeDefinition, sourceNode);
// Setting result.
compositeDefinition.setParameterValue(PARAM_RESULT, renditionAssoc);
}
else
{
String msg = "This method requires that the RenditionDefinition be of type CompositeRenditionDefinition";
logger.warn(msg);
throw new RenditionServiceException(msg);
}
}
private ChildAssociationRef executeCompositeRendition(CompositeRenditionDefinition definition, NodeRef sourceNode)
{
NodeRef source = sourceNode;
ChildAssociationRef result = null;
QName assocType = definition.getRenditionAssociationType();
NodeRef parent = definition.getRenditionParent();
for (RenditionDefinition subDefinition : definition.getActions())
{
ChildAssociationRef newResult = executeSubDefinition(source, subDefinition, parent, assocType);
if (result != null)
{
// Clean up temporary renditions.
nodeService.removeChild(parent, result.getChildRef());
}
result = newResult;
source = newResult.getChildRef();
}
return result;
}
private ChildAssociationRef executeSubDefinition(NodeRef source,//
RenditionDefinition subDefinition,//
NodeRef parent,//
QName assocType)
{
subDefinition.setRenditionParent(parent);
subDefinition.setRenditionAssociationType(assocType);
actionService.executeAction(subDefinition, source);
ChildAssociationRef newResult = (ChildAssociationRef) subDefinition.getParameterValue(PARAM_RESULT);
return newResult;
}
/*
* @see
* org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org
* .alfresco
* .repo.rendition.executer.AbstractRenderingEngine.RenderingContext)
*/
@Override
protected void render(RenderingContext data)
{
throw new RenditionServiceException("This method should never be caleld!");
}
/**
* @param actionService the actionService to set
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import java.util.Collection;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.content.transform.magick.ImageCropOptions;
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.TransformationOptions;
/**
* This class is the implementation of the {@link RenditionService}'s
* "imageRenderingEngine" rendering engine. This action renders a piece of
* content in the same MIME type as its source node, having been rescaled as
* requested.
*
* @author Neil McErlean
* @since 3.3
*/
public class ImageRenderingEngine extends AbstractTransformationRenderingEngine
{
// TODO This rendering engine should only take input of MIME type image/*
// However, we'll defer the addition of an EngineInputFilter until after
// Sprint 3.
public static final String NAME = "imageRenderingEngine";
// Resize params
public static final String PARAM_RESIZE_WIDTH = "xsize";
public static final String PARAM_RESIZE_HEIGHT = "ysize";
public static final String PARAM_IS_PERCENT_RESIZE = "isAbsolute";
public static final String PARAM_MAINTAIN_ASPECT_RATIO = "maintainAspectRatio";
public static final String PARAM_RESIZE_TO_THUMBNAIL = "resizeToThumbnail";
// Crop params
public static final String PARAM_CROP_WIDTH = "crop_width";
public static final String PARAM_CROP_HEIGHT = "crop_height";
public static final String PARAM_CROP_X_OFFSET = "crop_x";
public static final String PARAM_CROP_Y_OFFSET = "crop_y";
public static final String PARAM_CROP_GRAVITY = "crop_gravity";
public static final String PARAM_IS_PERCENT_CROP = "percent_crop";
public static final String PARAM_COMMAND_OPTIONS = "commandOptions";
/*
* @seeorg.alfresco.repo.rendition.executer.ReformatRenderingEngine#
* getTransformOptions
* (org.alfresco.repo.rendition.executer.AbstractRenderingEngine
* .RenderingContext)
*/
@Override
protected TransformationOptions getTransformOptions(RenderingContext context)
{
String commandOptions = context.getCheckedParam(PARAM_COMMAND_OPTIONS, String.class);
ImageResizeOptions imageResizeOptions = getImageResizeOptions(context);
ImageCropOptions cropOptions = getImageCropOptions(context);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions);
imageTransformationOptions.setCropOptions(cropOptions);
if (commandOptions != null)
{
imageTransformationOptions.setCommandOptions(commandOptions);
}
return imageTransformationOptions;
}
/*
* @seeorg.alfresco.repo.rendition.executer.ReformatRenderingEngine#
* getTargetMimeType
* (org.alfresco.repo.rendition.executer.AbstractRenderingEngine
* .RenderingContext)
*/
@Override
protected String getTargetMimeType(RenderingContext context)
{
String sourceMimeType = context.makeContentReader().getMimetype();
return context.getParamWithDefault(PARAM_MIME_TYPE, sourceMimeType);
}
private ImageResizeOptions getImageResizeOptions(RenderingContext context)
{
int newHeight = context.getIntegerParam(PARAM_RESIZE_WIDTH, -1);
int newWidth = context.getIntegerParam(PARAM_RESIZE_HEIGHT, -1);
if (newHeight == -1 && newWidth == -1)
{
return null; // Image is not being resized!
}
boolean isPercentResize = context.getParamWithDefault(PARAM_IS_PERCENT_RESIZE, false);
boolean maintainAspectRatio = context.getParamWithDefault(PARAM_MAINTAIN_ASPECT_RATIO, false);
ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setMaintainAspectRatio(maintainAspectRatio);
imageResizeOptions.setWidth(newHeight);
imageResizeOptions.setHeight(newWidth);
imageResizeOptions.setPercentResize(isPercentResize);
return imageResizeOptions;
}
private ImageCropOptions getImageCropOptions(RenderingContext context)
{
int newWidth = context.getIntegerParam(PARAM_CROP_WIDTH, -1);
int newHeight = context.getIntegerParam(PARAM_CROP_HEIGHT, -1);
if (newHeight == -1 && newWidth == -1)
{
return null;
}
int xOffset = context.getIntegerParam(PARAM_CROP_X_OFFSET, 0);
int yOffset = context.getIntegerParam(PARAM_CROP_Y_OFFSET, 0);
boolean isPercentCrop = context.getParamWithDefault(PARAM_IS_PERCENT_CROP, false);
String gravity = context.getCheckedParam(PARAM_CROP_GRAVITY, String.class);
ImageCropOptions cropOptions = new ImageCropOptions();
cropOptions.setGravity(gravity);
cropOptions.setHeight(newHeight);
cropOptions.setPercentageCrop(isPercentCrop);
cropOptions.setWidth(newWidth);
cropOptions.setXOffset(xOffset);
cropOptions.setYOffset(yOffset);
return cropOptions;
}
@Override
protected void checkParameterValues(Action action)
{
// Some numerical parameters should not be zero or negative.
checkNumericalParameterIsPositive(action, PARAM_RESIZE_WIDTH);
checkNumericalParameterIsPositive(action, PARAM_RESIZE_HEIGHT);
checkNumericalParameterIsPositive(action, PARAM_CROP_HEIGHT);
checkNumericalParameterIsPositive(action, PARAM_CROP_WIDTH);
// Target mime type should only be an image MIME type
String mimeTypeParam = (String)action.getParameterValue(PARAM_MIME_TYPE);
if (mimeTypeParam != null && !mimeTypeParam.startsWith("image"))
{
StringBuilder msg = new StringBuilder();
msg.append("Parameter ").append(PARAM_MIME_TYPE)
.append(" had illegal non-image MIME type: ").append(mimeTypeParam);
throw new IllegalArgumentException(msg.toString());
}
}
/**
* This method checks that if the specified parameter is non-null, that it has a
* positive numerical value. That is it is non-zero and positive.
*
* @param action
* @param numericalParamName must be an instance of java.lang.Number or null.
*/
private void checkNumericalParameterIsPositive(Action action, String numericalParamName)
{
Number param = (Number)action.getParameterValue(numericalParamName);
if (param != null && param.longValue() <= 0)
{
StringBuilder msg = new StringBuilder();
msg.append("Parameter ").append(numericalParamName)
.append(" had illegal non-positive value: ").append(param.intValue());
throw new IllegalArgumentException(msg.toString());
}
}
/*
* @seeorg.alfresco.repo.rendition.executer.AbstractRenderingEngine#
* getParameterDefinitions()
*/
@Override
protected Collection<ParameterDefinition> getParameterDefinitions()
{
Collection<ParameterDefinition> paramList = super.getParameterDefinitions();
//Resize Params
paramList.add(new ParameterDefinitionImpl(PARAM_RESIZE_WIDTH, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_RESIZE_WIDTH)));
paramList.add(new ParameterDefinitionImpl(PARAM_RESIZE_HEIGHT, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_RESIZE_HEIGHT)));
paramList.add(new ParameterDefinitionImpl(PARAM_IS_PERCENT_RESIZE, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_IS_PERCENT_RESIZE)));
paramList.add(new ParameterDefinitionImpl(PARAM_MAINTAIN_ASPECT_RATIO, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_MAINTAIN_ASPECT_RATIO)));
paramList.add(new ParameterDefinitionImpl(PARAM_RESIZE_TO_THUMBNAIL, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_RESIZE_TO_THUMBNAIL)));
//Crop Params
paramList.add(new ParameterDefinitionImpl(PARAM_CROP_GRAVITY, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_CROP_GRAVITY)));
paramList.add(new ParameterDefinitionImpl(PARAM_CROP_HEIGHT, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_CROP_HEIGHT)));
paramList.add(new ParameterDefinitionImpl(PARAM_CROP_WIDTH, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_CROP_WIDTH)));
paramList.add(new ParameterDefinitionImpl(PARAM_CROP_X_OFFSET, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_CROP_X_OFFSET)));
paramList.add(new ParameterDefinitionImpl(PARAM_CROP_Y_OFFSET, DataTypeDefinition.INT, false,
getParamDisplayLabel(PARAM_CROP_Y_OFFSET)));
paramList.add(new ParameterDefinitionImpl(PARAM_IS_PERCENT_CROP, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_IS_PERCENT_CROP)));
paramList.add(new ParameterDefinitionImpl(PARAM_COMMAND_OPTIONS, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_COMMAND_OPTIONS)));
return paramList;
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import java.util.Collection;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This class is the implementation of the {@link RenditionService}'s "reformat"
* action/rendering. This action renders a piece of content in the specified
* target MIME type. This is achieved using one of the standard transformers
* within the {@link ContentService}.
* <P/>
* Reformatting in this way is a simple conversion of one MIME type to another
* MIME type, without any other changes to the content. Therefore there is no
* support within this action for altering the content e.g. image
* cropping/resizing.
*
* @author Neil McErlean
* @since 3.3
*/
public class ReformatRenderingEngine extends AbstractTransformationRenderingEngine
{
private static Log logger = LogFactory.getLog(ReformatRenderingEngine.class);
/**
* This parameter is only necessary when converting from pdf to flash.
*/
public static final String PARAM_FLASH_VERSION = "flashVersion";
/*
* Action constants
*/
public static final String NAME = "reformat";
@Override
protected String getTargetMimeType(RenderingContext context)
{
String targetMimeType = context.getCheckedParam(PARAM_MIME_TYPE, String.class);
if (targetMimeType == null)
{
String msg = "The parameter: " + PARAM_MIME_TYPE + " must be explicitly set for this rendering engine!";
logger.warn(msg);
throw new RenditionServiceException(msg);
}
return targetMimeType;
}
@Override
protected TransformationOptions getTransformOptions(RenderingContext context)
{
NodeRef sourceNode = context.getSourceNode();
NodeRef destinationNode = context.getDestinationNode();
return new TransformationOptions(sourceNode, null, destinationNode, null);
}
/*
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#
* getParameterDefinitions()
*/
@Override
protected Collection<ParameterDefinition> getParameterDefinitions()
{
Collection<ParameterDefinition> paramList = super.getParameterDefinitions();
paramList.add(new ParameterDefinitionImpl(PARAM_MIME_TYPE, DataTypeDefinition.TEXT, true,
getParamDisplayLabel(PARAM_MIME_TYPE)));
paramList.add(new ParameterDefinitionImpl(PARAM_FLASH_VERSION, DataTypeDefinition.TEXT, false,
getParamDisplayLabel(PARAM_FLASH_VERSION)));
return paramList;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateService;
/**
* @author Nick Smith
*/
public class TemplateModelHelper
{
public static final String KEY_NODE = "node";
private final Repository repository;
private final ServiceRegistry serviceRegistry;
private final TemplateService templateService;
/**
* @param templateService
* @param repository
* @param serviceRegistry
*/
public TemplateModelHelper(TemplateService templateService, Repository repository, ServiceRegistry serviceRegistry)
{
super();
this.templateService = templateService;
this.repository = repository;
this.serviceRegistry = serviceRegistry;
}
/**
* Builds up the model map used by the {@link TemplateService} to process
* FreeMarker templates.
*
* @param sourceNode the node containing the content to be processed by the
* template.
* @param templateNode the node containing the template. Can be
* <code>null</code>.
* @param imgResolver the image resolver used to process images. Can be
* <code>null</code>.
* @param paramMap a map of parameters to add to the model. Can be
* <code>null</code>.
* @return the populated model {@link Map}.
*/
public Map<String, Object> buildModelMap(NodeRef sourceNode,//
NodeRef templateNode,//
TemplateImageResolver imgResolver,//
Map<String, Serializable> paramMap)
{
Map<String, Object> model = buildDefaultModel(templateNode, imgResolver);
TemplateNode sourceTemplateNode = new TemplateNode(sourceNode, serviceRegistry, null);
// TODO Add xml dom here.
// model.put("xml", NodeModel.wrap(null));
model.put(KEY_NODE, sourceTemplateNode);
if (paramMap != null)
model.putAll(paramMap);
return model;
}
/**
* Builds the default model populated with the current user, company home
* and user home.
*
* @param templateNode the node containing the template. Can be
* <code>null</code>.
* @param imgResolver the image resolver used to process images. Can be
* <code>null</code>.
* @return the default model {@link Map}.
*/
public Map<String, Object> buildDefaultModel(NodeRef templateNode, TemplateImageResolver imgResolver)
{
// The templateNode can be null.
NodeRef companyHome = repository.getCompanyHome();
// The fully authenticated user below is the username of the person who logged in and
// who requested the execution of the current rendition. This will not be the
// same person as the current user as renditions are executed by the system user.
String fullyAuthenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser();
NodeRef person = serviceRegistry.getPersonService().getPerson(fullyAuthenticatedUser);
NodeRef userHome = repository.getUserHome(person);
Map<String, Object> model = templateService.buildDefaultModel(person, companyHome, userHome, templateNode,
imgResolver);
return model;
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.rendition.executer;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
import java.util.Collection;
import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* @author Nick Smith
*/
public class TemplatingRenderingEngine//
extends AbstractRenderingEngine//
implements InitializingBean
{
private final static Log log = LogFactory.getLog(TemplatingRenderingEngine.class);
public static final String NAME = "templatingRenderingEngine";
public static final String PARAM_MODEL = "model";
public static final String PARAM_TEMPLATE = "template_string";
public static final String PARAM_TEMPLATE_NODE = "template_node";
public static final String PARAM_TEMPLATE_PATH = "template_path";
private static final String PARAM_IMAGE_RESOLVER = "image_resolver";
private TemplateService templateService;
private Repository repository;
private ServiceRegistry serviceRegistry;
private TemplateModelHelper modelHelper;
/*
* @see
* org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org
* .alfresco.service.cmr.repository.NodeRef,
* org.alfresco.service.cmr.rendition.RenditionDefinition,
* org.alfresco.service.cmr.repository.ContentReader,
* org.alfresco.service.cmr.repository.ChildAssociationRef)
*/
@SuppressWarnings("unchecked")
@Override
protected void render(RenderingContext context)
{
NodeRef sourceNode = context.getSourceNode();
NodeRef templateNode = getTemplateNode(context);
Map<String, Serializable> paramMap = context.getCheckedParam(PARAM_MODEL, Map.class);
TemplateImageResolver imgResolver = context.getCheckedParam(PARAM_IMAGE_RESOLVER, TemplateImageResolver.class);
Map<String, Object> model = modelHelper.buildModelMap(sourceNode, templateNode, imgResolver, paramMap);
processTemplate(context, templateNode, model);
}
private void processTemplate(RenderingContext context, NodeRef templateNode, Map<String, Object> model)
{
String template = context.getCheckedParam(PARAM_TEMPLATE, String.class);
if ((template == null) && (templateNode == null))
{
throwTemplateParamsNotFoundException();
}
ContentWriter contentWriter = context.makeContentWriter();
Writer writer = new OutputStreamWriter(contentWriter.getContentOutputStream());
try
{
if (template != null)
{
templateService.processTemplateString("freemarker", template, model, writer);
}
else if (templateNode != null)
{
templateService.processTemplate("freemarker", templateNode.toString(), model, writer);
}
}
finally
{
try
{
writer.close();
} catch (IOException ex)
{
// Nothing that can be done. Log it and move on.
log.warn("Failed to close content writer: ", ex);
}
}
}
private void throwTemplateParamsNotFoundException()
{
StringBuilder msg = new StringBuilder("This action requires that either the ");
msg.append(PARAM_TEMPLATE);
msg.append(" parameter or the ");
msg.append(PARAM_TEMPLATE_NODE);
msg.append(" parameter be specified. ");
throw new RenditionServiceException(msg.toString());
}
private NodeRef getTemplateNode(RenderingContext context)
{
NodeRef node = context.getCheckedParam(PARAM_TEMPLATE_NODE, NodeRef.class);
if (node == null)
{
String path = context.getCheckedParam(PARAM_TEMPLATE_PATH, String.class);
if (path != null && path.length() > 0)
{
SearchService searchService = serviceRegistry.getSearchService();
StoreRef storeRef = context.getDestinationNode().getStoreRef();
ResultSet result = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, path);
if (result.length() != 1)
{
throw new RenditionServiceException("Could not find template node for path: " + path);
}
node = result.getNodeRef(0);
}
}
return node;
}
/*
* @seeorg.alfresco.repo.rendition.executer.AbstractRenderingEngine#
* getParameterDefinitions()
*/
@Override
protected Collection<ParameterDefinition> getParameterDefinitions()
{
Collection<ParameterDefinition> paramList = super.getParameterDefinitions();
ParameterDefinitionImpl modelParamDef = new ParameterDefinitionImpl(//
PARAM_MODEL,//
DataTypeDefinition.ANY,//
false,//
getParamDisplayLabel(PARAM_MODEL));
ParameterDefinitionImpl templateParamDef = new ParameterDefinitionImpl(//
PARAM_TEMPLATE,//
DataTypeDefinition.TEXT,//
false,//
getParamDisplayLabel(PARAM_TEMPLATE));
ParameterDefinitionImpl templateNodeParamDef = new ParameterDefinitionImpl(//
PARAM_TEMPLATE_NODE,//
DataTypeDefinition.NODE_REF,//
false,//
getParamDisplayLabel(PARAM_TEMPLATE_NODE));
ParameterDefinitionImpl templatePathParamDef = new ParameterDefinitionImpl(//
PARAM_TEMPLATE_PATH,//
DataTypeDefinition.TEXT,//
false,//
getParamDisplayLabel(PARAM_TEMPLATE_PATH));
ParameterDefinitionImpl imgResolverParamDef = new ParameterDefinitionImpl(//
PARAM_IMAGE_RESOLVER,//
DataTypeDefinition.ANY,//
false,//
getParamDisplayLabel(PARAM_IMAGE_RESOLVER));
paramList.add(modelParamDef);
paramList.add(templateParamDef);
paramList.add(templateNodeParamDef);
paramList.add(templatePathParamDef);
paramList.add(imgResolverParamDef);
return paramList;
}
/**
* @param templateService the templateService to set
*/
public void setTemplateService(TemplateService templateService)
{
this.templateService = templateService;
}
/**
* @param repository the repository to set
*/
public void setRepositoryHelper(Repository repository)
{
this.repository = repository;
}
/**
* @param serviceRegistry the serviceRegistry to set
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* Sets up {@link TemplateModelHelper}.
*/
public void afterPropertiesSet()
{
this.modelHelper = new TemplateModelHelper(templateService, repository, serviceRegistry);
}
}

View File

@@ -0,0 +1,59 @@
<h6>Test Template 1</h6>
<#-- Title is used in test code to ensure that updates to source node rerender properly -->
<#-- The xxx xxx demarcation here is used by the test code -->
TestTitle= xxx${node.properties["cm:title"]?string}xxx
<#-- Test basic properties -->
${node.id}<br>
${node.name}<br>
${node.properties?size}<br>
${node.children?size}<br>
<#if node.assocs["cm:translations"]?exists>
node.assocs<br>
</#if>
${node.aspects?size}<br>
<#if node.isContainer>node.isContainer</#if><br>
<#if node.isDocument>node.isDocument</#if><br>
<#--${node.content}<br>-->
${node.url}<br>
${node.displayPath}<br>
${node.icon16}<br>
${node.icon32}<br>
<#if node.mimetype?exists>node.mimetype</#if><br>
<#if node.size?exists>node.size</#if><br>
<#if node.isLocked>node.isLocked</#if><br>
<#-- Test child walking and property resolving -->
<table>
<#list node.children as child>
<#-- show properties of each child -->
<#assign props = child.properties?keys>
<#list props as t>
<#-- If the property exists -->
<#if child.properties[t]?exists>
<#-- If it is a date, format it accordingly-->
<#if child.properties[t]?is_date>
<tr><td>${t} = ${child.properties[t]?date}</td></tr>
<#-- If it is a boolean, format it accordingly-->
<#elseif child.properties[t]?is_boolean>
<tr><td>${t} = ${child.properties[t]?string("yes", "no")}</td></tr>
<#-- Otherwise treat it as a string -->
<#else>
<tr><td>${t} = ${child.properties[t]}</td></tr>
</#if>
</#if>
</#list>
</#list>
</table>
<#-- Test XPath -->
<#list node.childrenByXPath["//*[@sys:store-protocol='workspace']"] as child>
${child.name}
</#list>
<h6>End Test Template 1</h6>

View File

@@ -44,6 +44,7 @@ import org.springframework.context.ApplicationContext;
*/ */
public class TemplateServiceImplTest extends TestCase public class TemplateServiceImplTest extends TestCase
{ {
private static final String TEMPLATE_1 = "org/alfresco/repo/template/test_template1.ftl";
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TemplateService templateService; private TemplateService templateService;
@@ -127,5 +128,4 @@ public class TemplateServiceImplTest extends TestCase
}); });
} }
private static final String TEMPLATE_1 = "org/alfresco/repo/template/test_template1.ftl";
} }

View File

@@ -19,30 +19,43 @@
package org.alfresco.repo.thumbnail; package org.alfresco.repo.thumbnail;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.thumbnail.ThumbnailException; import org.alfresco.service.cmr.thumbnail.ThumbnailException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/** /**
* Registry of all the thumbnail details available * Registry of all the thumbnail details available
* *
* @author Roy Wetherall * @author Roy Wetherall
* @author Neil McErlean
*/ */
public class ThumbnailRegistry public class ThumbnailRegistry
{ {
/** Content service */ /** Content service */
private ContentService contentService; private ContentService contentService;
/** Map of thumbnail defintion */ /** Rendition service */
private Map<String, ThumbnailDefinition> thumbnailDefinitions = new HashMap<String, ThumbnailDefinition>(7); private RenditionService renditionService;
private List<String> thumbnails;
/** This flag indicates whether the thumbnail definitions have been lazily loaded or not. */
private boolean thumbnailDefinitionsInited = false;
/** Map of thumbnail definition */
private Map<String, ThumbnailDefinition> thumbnailDefinitions = new HashMap<String, ThumbnailDefinition>();
/** Cache to store mimetype to thumbnailDefinition mapping */ /** Cache to store mimetype to thumbnailDefinition mapping */
private Map<String, List<ThumbnailDefinition>> mimetypeMap = new HashMap<String, List<ThumbnailDefinition>>(17); private Map<String, List<ThumbnailDefinition>> mimetypeMap = new HashMap<String, List<ThumbnailDefinition>>(17);
/** /**
* Content service * Content service
* *
@@ -54,36 +67,63 @@ public class ThumbnailRegistry
} }
/** /**
* Add a number of thumbnail defintions * Rendition service
* *
* @param thumbnailDefinitions list of thumbnail details * @param renditionService rendition service
*/ */
public void setThumbnailDefinitions(List<ThumbnailDefinition> thumbnailDefinitions) public void setRenditionService(RenditionService renditionService)
{ {
for (ThumbnailDefinition value : thumbnailDefinitions) this.renditionService = renditionService;
{ }
addThumbnailDefinition(value);
} public void setThumbnails(final List<String> thumbnails)
{
this.thumbnails = thumbnails;
// We'll not populate the data fields in the ThumbnailRegistry here, instead preferring
// to do it lazily later.
} }
/** /**
* Get a list of all the thumbnail defintions * Get a list of all the thumbnail definitions
* *
* @return Collection<ThumbnailDefinition> colleciton of thumbnail defintions * @return Collection<ThumbnailDefinition> collection of thumbnail definitions
*/ */
public List<ThumbnailDefinition> getThumbnailDefinitions() public List<ThumbnailDefinition> getThumbnailDefinitions()
{ {
if (thumbnailDefinitionsInited == false)
{
this.initThumbnailDefinitions();
thumbnailDefinitionsInited = true;
}
return new ArrayList<ThumbnailDefinition>(this.thumbnailDefinitions.values()); return new ArrayList<ThumbnailDefinition>(this.thumbnailDefinitions.values());
} }
/** private void initThumbnailDefinitions()
*
* @param mimetype
* @return
*/
public List<ThumbnailDefinition> getThumnailDefintions(String mimetype)
{ {
List<ThumbnailDefinition> result = this.mimetypeMap.get(mimetype);; ThumbnailRenditionConvertor thumbnailRenditionConvertor = new ThumbnailRenditionConvertor();
for (String thumbnailDefinitionName : this.thumbnails)
{
QName qName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbnailDefinitionName);
RenditionDefinition rAction = renditionService
.loadRenditionDefinition(qName);
ThumbnailDefinition thDefn = thumbnailRenditionConvertor.convert(rAction);
thumbnailDefinitions.put(thumbnailDefinitionName, thDefn);
}
}
public List<ThumbnailDefinition> getThumbnailDefinitions(String mimetype)
{
if (thumbnailDefinitionsInited == false)
{
this.initThumbnailDefinitions();
thumbnailDefinitionsInited = true;
}
List<ThumbnailDefinition> result = this.mimetypeMap.get(mimetype);
if (result == null) if (result == null)
{ {
@@ -107,12 +147,28 @@ public class ThumbnailRegistry
} }
/** /**
* Add a thumnail details *
* @param mimetype
* @return
* @deprecated Use {@link #getThumbnailDefinitions(String)} instead.
*/
public List<ThumbnailDefinition> getThumnailDefintions(String mimetype)
{
return this.getThumbnailDefinitions(mimetype);
}
/**
* Add a thumbnail details
* *
* @param thumbnailDetails thumbnail details * @param thumbnailDetails thumbnail details
*/ */
public void addThumbnailDefinition(ThumbnailDefinition thumbnailDetails) public void addThumbnailDefinition(ThumbnailDefinition thumbnailDetails)
{ {
if (thumbnailDefinitionsInited == false)
{
this.initThumbnailDefinitions();
thumbnailDefinitionsInited = true;
}
String thumbnailName = thumbnailDetails.getName(); String thumbnailName = thumbnailDetails.getName();
if (thumbnailName == null) if (thumbnailName == null)
{ {
@@ -130,6 +186,11 @@ public class ThumbnailRegistry
*/ */
public ThumbnailDefinition getThumbnailDefinition(String thumbnailName) public ThumbnailDefinition getThumbnailDefinition(String thumbnailName)
{ {
if (thumbnailDefinitionsInited == false)
{
this.initThumbnailDefinitions();
thumbnailDefinitionsInited = true;
}
return this.thumbnailDefinitions.get(thumbnailName); return this.thumbnailDefinitions.get(thumbnailName);
} }
} }

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.thumbnail;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.content.transform.swf.SWFTransformationOptions;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.repo.rendition.executer.ReformatRenderingEngine;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
/**
* A helper class to convert {@link TransformationOptions transformationOptions} (a thumbnail-specific
* class) to rendition-specific parameters and vice versa.
*
* @author Neil McErlean
*/
public class ThumbnailRenditionConvertor
{
public Map<String, Serializable> convert(TransformationOptions transformationOptions, ThumbnailParentAssociationDetails assocDetails)
{
Map<String, Serializable> parameters = new HashMap<String, Serializable>();
// parameters common to all transformations
putParameterIfNotNull(AbstractRenderingEngine.PARAM_SOURCE_CONTENT_PROPERTY, transformationOptions.getSourceContentProperty(), parameters);
putParameterIfNotNull(AbstractRenderingEngine.PARAM_TARGET_CONTENT_PROPERTY, transformationOptions.getTargetContentProperty(), parameters);
putParameterIfNotNull(RenditionService.PARAM_DESTINATION_NODE, transformationOptions.getTargetNodeRef(), parameters);
// putParameterIfNotNull(ImageRenderingEngine.PARAM_ASSOC_NAME, assocDetails.getAssociationName(), parameters);
// putParameterIfNotNull(ImageRenderingEngine.PARAM_ASSOC_TYPE, assocDetails.getAssociationType(), parameters);
if (transformationOptions instanceof SWFTransformationOptions)
{
SWFTransformationOptions swfTransformationOptions = (SWFTransformationOptions)transformationOptions;
putParameterIfNotNull(ReformatRenderingEngine.PARAM_FLASH_VERSION, swfTransformationOptions.getFlashVersion(), parameters);
}
else if (transformationOptions instanceof ImageTransformationOptions)
{
ImageTransformationOptions imTransformationOptions = (ImageTransformationOptions)transformationOptions;
putParameterIfNotNull(ImageRenderingEngine.PARAM_COMMAND_OPTIONS, imTransformationOptions.getCommandOptions(), parameters);
ImageResizeOptions imgResizeOptions = imTransformationOptions.getResizeOptions();
if (imgResizeOptions != null)
{
int width = imgResizeOptions.getWidth();
parameters.put(ImageRenderingEngine.PARAM_RESIZE_WIDTH, width);
int height = imgResizeOptions.getHeight();
parameters.put(ImageRenderingEngine.PARAM_RESIZE_HEIGHT, height);
boolean maintainAspectRatio = imgResizeOptions.isMaintainAspectRatio();
parameters.put(ImageRenderingEngine.PARAM_MAINTAIN_ASPECT_RATIO, maintainAspectRatio);
boolean percentResize = imgResizeOptions.isPercentResize();
parameters.put(ImageRenderingEngine.PARAM_IS_PERCENT_RESIZE, percentResize);
boolean resizeToThumbnail = imgResizeOptions.isResizeToThumbnail();
parameters.put(ImageRenderingEngine.PARAM_RESIZE_TO_THUMBNAIL, resizeToThumbnail);
}
}
return parameters;
}
private void putParameterIfNotNull(String paramName, Serializable paramValue, Map<String, Serializable> params)
{
if (paramValue != null)
{
params.put(paramName, paramValue);
}
}
public ThumbnailDefinition convert(RenditionDefinition renditionDefinition)
{
ThumbnailDefinition thDefn = new ThumbnailDefinition();
Map<String, Serializable> params = renditionDefinition.getParameterValues();
//parameters common to all the built-in thumbnail definitions
Serializable mimeTypeParam = params.get(AbstractRenderingEngine.PARAM_MIME_TYPE);
thDefn.setMimetype((String) mimeTypeParam);
thDefn.setName(renditionDefinition.getRenditionName().getLocalName());
Serializable placeHolderResourcePathParam = params.get(AbstractRenderingEngine.PARAM_PLACEHOLDER_RESOURCE_PATH);
if (placeHolderResourcePathParam != null)
{
thDefn.setPlaceHolderResourcePath((String)placeHolderResourcePathParam);
}
//TODO src/target contentProp & nodeRef
TransformationOptions transformationOptions = null;
Serializable flashVersion = renditionDefinition.getParameterValue(ReformatRenderingEngine.PARAM_FLASH_VERSION);
if (flashVersion != null)
{
// Thumbnails based on SWFTransformationOptions
transformationOptions = new SWFTransformationOptions();
SWFTransformationOptions swfTranOpts = (SWFTransformationOptions)transformationOptions;
swfTranOpts.setFlashVersion((String)flashVersion);
}
else
{
// Thumbnails based on ImageTransformationOptions
transformationOptions = new ImageTransformationOptions();
ImageTransformationOptions imgTrOpts = (ImageTransformationOptions)transformationOptions;
ImageResizeOptions resizeOptions = new ImageResizeOptions();
Serializable xsize = renditionDefinition.getParameterValue(ImageRenderingEngine.PARAM_RESIZE_WIDTH);
if (xsize != null)
{
// Saved actions with int parameters seem to be coming back as Longs. TODO Investigate
resizeOptions.setWidth(((Long) xsize).intValue());
}
Serializable ysize = renditionDefinition.getParameterValue(ImageRenderingEngine.PARAM_RESIZE_HEIGHT);
if (ysize != null)
{
resizeOptions.setHeight(((Long) ysize).intValue());
}
Serializable maintainAspectRatio = renditionDefinition.getParameterValue(ImageRenderingEngine.PARAM_MAINTAIN_ASPECT_RATIO);
if (maintainAspectRatio != null)
{
resizeOptions.setMaintainAspectRatio((Boolean) maintainAspectRatio);
}
Serializable resizeToThumbnail = renditionDefinition.getParameterValue(ImageRenderingEngine.PARAM_RESIZE_TO_THUMBNAIL);
if (resizeToThumbnail != null)
{
resizeOptions.setResizeToThumbnail((Boolean) resizeToThumbnail);
}
imgTrOpts.setResizeOptions(resizeOptions);
}
thDefn.setTransformationOptions(transformationOptions);
return thDefn;
}
}

View File

@@ -19,21 +19,24 @@
package org.alfresco.repo.thumbnail; package org.alfresco.repo.thumbnail;
import java.io.Serializable; import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.content.transform.swf.SWFTransformationOptions;
import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.repo.rendition.executer.ReformatRenderingEngine;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.repository.TransformationOptions;
@@ -42,14 +45,14 @@ import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.ParameterCheck;
/** /**
* @author Roy Wetherall * @author Roy Wetherall
* @author Neil McErlean
*/ */
public class ThumbnailServiceImpl implements ThumbnailService public class ThumbnailServiceImpl implements ThumbnailService
{ {
@@ -79,6 +82,19 @@ public class ThumbnailServiceImpl implements ThumbnailService
/** Thumbnail registry */ /** Thumbnail registry */
private ThumbnailRegistry thumbnailRegistry; private ThumbnailRegistry thumbnailRegistry;
/** Rendition service */
private RenditionService renditionService;
/**
* Set the rendition service.
*
* @param renditionService
*/
public void setRenditionService(RenditionService renditionService)
{
this.renditionService = renditionService;
}
/** /**
* Set the node service * Set the node service
* *
@@ -168,118 +184,89 @@ public class ThumbnailServiceImpl implements ThumbnailService
logger.debug("Creating thumbnail: There is already a thumbnail with the name '" + thumbnailName + "' (node=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; mimetype=" + mimetype); logger.debug("Creating thumbnail: There is already a thumbnail with the name '" + thumbnailName + "' (node=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; mimetype=" + mimetype);
} }
// We can't continue because there is already an thumbnail with the given name for that content property // We can't continue because there is already a thumbnail with the given name for that content property
throw new ThumbnailException(ERR_DUPLICATE_NAME); throw new ThumbnailException(ERR_DUPLICATE_NAME);
} }
NodeRef thumbnail = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<NodeRef>() ChildAssociationRef thumbnailRef = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<ChildAssociationRef>()
{ {
public NodeRef doWork() throws Exception public ChildAssociationRef doWork() throws Exception
{ {
NodeRef thumbnail; // Need a variable scoped to this inner class in order to assign a new value to it.
String localThumbnailName = thumbnailName;
// Apply the thumbnailed aspect to the node if it doesn't already have it
if (nodeService.hasAspect(node, ContentModel.ASPECT_THUMBNAILED) == false)
{
// Ensure we do not update the 'modifier' due to thumbnail addition
behaviourFilter.disableBehaviour(node, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.addAspect(node, ContentModel.ASPECT_THUMBNAILED, null);
}
finally
{
behaviourFilter.enableBehaviour(node, ContentModel.ASPECT_AUDITABLE);
}
}
// Get the name of the thumbnail and add to properties map // Get the name of the thumbnail and add to properties map
String thumbName = thumbnailName; if (localThumbnailName == null || localThumbnailName.length() == 0)
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(4);
if (thumbName == null || thumbName.length() == 0)
{ {
thumbName = GUID.generate(); localThumbnailName = GUID.generate();
} }
else
{
String thumbnailFileName = generateThumbnailFileName(thumbName, mimetype);
properties.put(ContentModel.PROP_NAME, thumbnailFileName);
}
properties.put(ContentModel.PROP_THUMBNAIL_NAME, thumbName);
// Add the name of the content property // We're prepending the cm namespace here.
properties.put(ContentModel.PROP_CONTENT_PROPERTY_NAME, contentProperty); QName thumbnailQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, localThumbnailName);
// See if parent association details have been specified for the thumbnail
if (assocDetails == null)
{
// Create the thumbnail using the thumbnails child association
thumbnail = nodeService.createNode(
node,
ContentModel.ASSOC_THUMBNAILS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbName),
ContentModel.TYPE_THUMBNAIL,
properties).getChildRef();
}
else
{
// Create the thumbnail using the specified parent assoc details
thumbnail = nodeService.createNode(
assocDetails.getParent(),
assocDetails.getAssociationType(),
assocDetails.getAssociationName(),
ContentModel.TYPE_THUMBNAIL,
properties).getChildRef();
// Associate the new thumbnail to the source RenditionDefinition renderingAction = renditionService.loadRenditionDefinition(thumbnailQName);
nodeService.addChild( if (renderingAction == null)
node,
thumbnail,
ContentModel.ASSOC_THUMBNAILS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbName));
}
// Get the content reader and writer for content nodes
ContentReader reader = contentService.getReader(node, contentProperty);
ContentWriter writer = contentService.getWriter(thumbnail, ContentModel.PROP_CONTENT, true);
writer.setMimetype(mimetype);
writer.setEncoding(reader.getEncoding());
// Catch the failure to create the thumbnail
if (contentService.isTransformable(reader, writer, transformationOptions) == false)
{ {
if (logger.isDebugEnabled() == true) // If the provided thumbnailName does not map to a built-in rendition definition
{ // then we must create a dynamic rendition definition for this thumbnail.
logger.debug("Creating thumbnail: There is no transformer to generate the thumbnail required (node=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; mimetype=" + mimetype + ")"); // To do this we must have a renderingEngineName.
} //
// The transformation will either be a imageRenderingEngine or a reformat (pdf2swf)
String renderingEngineName = getRenderingEngineNameFor(transformationOptions);
// Throw exception indicating that the thumbnail could not be created renderingAction = renditionService.createRenditionDefinition(thumbnailQName, renderingEngineName);
throw new ThumbnailException(MessageFormat.format(ERR_NO_CREATE, reader.getMimetype(), writer.getMimetype()));
} }
else Map<String, Serializable> params = new ThumbnailRenditionConvertor().convert(transformationOptions, assocDetails);
for (String key : params.keySet())
{ {
// Do the thumbnail transformation renderingAction.setParameterValue(key, params.get(key));
contentService.transform(reader, writer, transformationOptions);
} }
return thumbnail; ChildAssociationRef chAssRef = null;
try
{
chAssRef = renditionService.render(node, renderingAction);
} catch (RenditionServiceException rsx)
{
throw new ThumbnailException(rsx.getMessage(), rsx);
}
return chAssRef;
} }
}, AuthenticationUtil.getSystemUserName()); }, AuthenticationUtil.getSystemUserName());
// Return the created thumbnail // Return the created thumbnail
return thumbnail; return getThumbnailNode(thumbnailRef);
}
private String getRenderingEngineNameFor(TransformationOptions options)
{
if (options instanceof ImageTransformationOptions)
{
return ImageRenderingEngine.NAME;
}
else if (options instanceof SWFTransformationOptions)
{
return ReformatRenderingEngine.NAME;
}
else
{
// TODO What can we do here? Can we treat this as an error?
// Isn't this a 'standard' TransformationOptions?
return "";
}
} }
/** /**
* Generates the thumbnail name from the name and destination mimertype * This method returns the NodeRef for the thumbnail, using the ChildAssociationRef
* that links the sourceNode to its associated Thumbnail node.
* *
* @param thumbnailName the thumbnail name * @param thumbnailRef the ChildAssociationRef containing the child NodeRef.
* @param destinationMimetype the destination name * @return the NodeRef of the thumbnail itself.
* @return String the thumbnail file name
*/ */
private String generateThumbnailFileName(String thumbnailName, String destinationMimetype) public NodeRef getThumbnailNode(ChildAssociationRef thumbnailRef)
{ {
return thumbnailName + "." + this.mimetypeMap.getExtension(destinationMimetype); return thumbnailRef.getChildRef();
} }
/** /**
@@ -287,75 +274,61 @@ public class ThumbnailServiceImpl implements ThumbnailService
*/ */
public void updateThumbnail(final NodeRef thumbnail, final TransformationOptions transformationOptions) public void updateThumbnail(final NodeRef thumbnail, final TransformationOptions transformationOptions)
{ {
// Seeing as how this always gives its own options, maybe this should be a delete and recreate?
if (logger.isDebugEnabled() == true) if (logger.isDebugEnabled() == true)
{ {
logger.debug("Updating thumbnail (thumbnail=" + thumbnail.toString() + ")"); logger.debug("Updating thumbnail (thumbnail=" + thumbnail.toString() + ")");
} }
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>() // First check that we are dealing with a rendition object
if (renditionService.isRendition(thumbnail))
{ {
public Object doWork() throws Exception // Get the node that is the source of the thumbnail
ChildAssociationRef parentAssoc = renditionService.getSourceNode(thumbnail);
if (parentAssoc == null)
{ {
// First check that we are dealing with a thumbnail if (logger.isDebugEnabled() == true)
if (ContentModel.TYPE_THUMBNAIL.equals(nodeService.getType(thumbnail)) == true)
{ {
// Get the node that is the source of the thumbnail logger.debug("Updating thumbnail: The thumbnails parent cannot be found (thumbnail=" + thumbnail.toString() + ")");
NodeRef node = null;
List<ChildAssociationRef> parents = nodeService.getParentAssocs(thumbnail, ContentModel.ASSOC_THUMBNAILS, RegexQNamePattern.MATCH_ALL);
if (parents.size() == 0)
{
if (logger.isDebugEnabled() == true)
{
logger.debug("Updating thumbnail: The thumbnails parent cannot be found (thumbnail=" + thumbnail.toString() + ")");
}
throw new ThumbnailException(ERR_NO_PARENT);
}
else
{
node = parents.get(0).getParentRef();
}
// Get the content property
QName contentProperty = (QName)nodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT_PROPERTY_NAME);
// Get the reader and writer
ContentReader reader = contentService.getReader(node, contentProperty);
ContentWriter writer = contentService.getWriter(thumbnail, ContentModel.PROP_CONTENT, true);
// Set the basic detail of the transformation options
transformationOptions.setSourceNodeRef(node);
transformationOptions.setSourceContentProperty(contentProperty);
transformationOptions.setTargetNodeRef(thumbnail);
transformationOptions.setTargetContentProperty(ContentModel.PROP_CONTENT);
// Catch the failure to create the thumbnail
if (contentService.isTransformable(reader, writer, transformationOptions) == false)
{
if (logger.isDebugEnabled() == true)
{
logger.debug("Updating thumbnail: there is not transformer to update the thumbnail with (thumbnail=" + thumbnail.toString() + ")");
}
// Throw exception indicating that the thumbnail could not be created
throw new ThumbnailException(MessageFormat.format(ERR_NO_CREATE, reader.getMimetype(), writer.getMimetype()));
}
else
{
// Do the thumbnail transformation
contentService.transform(reader, writer, transformationOptions);
}
} }
else throw new ThumbnailException(ERR_NO_PARENT);
{
if (logger.isDebugEnabled() == true)
{
logger.debug("Updating thumbnail: cannot update a thumbnail node that isn't the correct thumbnail type (thumbnail=" + thumbnail.toString() + ")");
}
}
return null;
} }
}, AuthenticationUtil.getSystemUserName());
final QName renditionAssociationName = parentAssoc.getQName();
NodeRef sourceNode = parentAssoc.getParentRef();
// Get the content property
QName contentProperty = (QName)nodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT_PROPERTY_NAME);
// Set the basic detail of the transformation options
transformationOptions.setSourceNodeRef(sourceNode);
transformationOptions.setSourceContentProperty(contentProperty);
transformationOptions.setTargetContentProperty(ContentModel.PROP_CONTENT);
// Do the thumbnail transformation
RenditionDefinition rendDefn = renditionService.loadRenditionDefinition(renditionAssociationName);
if (rendDefn == null)
{
String renderingEngineName = getRenderingEngineNameFor(transformationOptions);
rendDefn = renditionService.createRenditionDefinition(parentAssoc.getQName(), renderingEngineName);
}
Map<String, Serializable> params = new ThumbnailRenditionConvertor().convert(transformationOptions, null);
for (String key : params.keySet())
{
rendDefn.setParameterValue(key, params.get(key));
}
renditionService.render(sourceNode, rendDefn);
}
else
{
if (logger.isDebugEnabled() == true)
{
logger.debug("Updating thumbnail: cannot update a thumbnail node that isn't the correct thumbnail type (thumbnail=" + thumbnail.toString() + ")");
}
}
} }
/** /**
@@ -363,8 +336,6 @@ public class ThumbnailServiceImpl implements ThumbnailService
*/ */
public NodeRef getThumbnailByName(NodeRef node, QName contentProperty, String thumbnailName) public NodeRef getThumbnailByName(NodeRef node, QName contentProperty, String thumbnailName)
{ {
NodeRef thumbnail = null;
// //
// NOTE: // NOTE:
// //
@@ -377,21 +348,21 @@ public class ThumbnailServiceImpl implements ThumbnailService
logger.debug("Getting thumbnail by name (nodeRef=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; thumbnailName=" + thumbnailName + ")"); logger.debug("Getting thumbnail by name (nodeRef=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; thumbnailName=" + thumbnailName + ")");
} }
// Check that the node has the thumbnailed aspect applied // Thumbnails have a cm: prefix.
if (nodeService.hasAspect(node, ContentModel.ASPECT_THUMBNAILED) == true) QName namespacedThumbnailName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbnailName);
ChildAssociationRef existingRendition = renditionService.getRenditionByName(node, namespacedThumbnailName);
NodeRef thumbnail = null;
// Check the child to see if it matches the content property we are concerned about.
// We can assume there will only ever be one per content property since createThumbnail enforces this.
if (existingRendition != null)
{ {
// Get all the thumbnails that match the thumbnail name NodeRef child = existingRendition.getChildRef();
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(node, ContentModel.ASSOC_THUMBNAILS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, thumbnailName)); Serializable contentPropertyName = this.nodeService.getProperty(child, ContentModel.PROP_CONTENT_PROPERTY_NAME);
for (ChildAssociationRef assoc : assocs) if (contentProperty.equals(contentPropertyName) == true)
{ {
// Check the child to see if it matches the content property we are concerned about. thumbnail = child;
// We can assume there will only ever be one per content property since createThumbnail enforces this.
NodeRef child = assoc.getChildRef();
if (contentProperty.equals(this.nodeService.getProperty(child, ContentModel.PROP_CONTENT_PROPERTY_NAME)) == true)
{
thumbnail = child;
break;
}
} }
} }
@@ -417,24 +388,21 @@ public class ThumbnailServiceImpl implements ThumbnailService
logger.debug("Getting thumbnails (nodeRef=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; mimetype=" + mimetype + ")"); logger.debug("Getting thumbnails (nodeRef=" + node.toString() + "; contentProperty=" + contentProperty.toString() + "; mimetype=" + mimetype + ")");
} }
// Check that the node has the thumbnailed aspect applied List<ChildAssociationRef> renditions = this.renditionService.getRenditions(node);
if (nodeService.hasAspect(node, ContentModel.ASPECT_THUMBNAILED) == true)
for (ChildAssociationRef assoc : renditions)
{ {
// Get all the thumbnails that match the thumbnail name // Check the child to see if it matches the content property we are concerned about.
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(node, ContentModel.ASSOC_THUMBNAILS, RegexQNamePattern.MATCH_ALL); // We can assume there will only ever be one per content property since createThumbnail enforces this.
for (ChildAssociationRef assoc : assocs) NodeRef child = assoc.getChildRef();
if (contentProperty.equals(this.nodeService.getProperty(child, ContentModel.PROP_CONTENT_PROPERTY_NAME)) == true &&
matchMimetypeOptions(child, mimetype, options) == true)
{ {
// Check the child to see if it matches the content property we are concerned about. thumbnails.add(child);
// We can assume there will only ever be one per content property since createThumbnail enforces this.
NodeRef child = assoc.getChildRef();
if (contentProperty.equals(this.nodeService.getProperty(child, ContentModel.PROP_CONTENT_PROPERTY_NAME)) == true &&
matchMimetypeOptions(child, mimetype, options) == true)
{
thumbnails.add(child);
}
} }
} }
//TODO Ensure this doesn't return non-thumbnail renditions.
return thumbnails; return thumbnails;
} }

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2005-2010 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.thumbnail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.rendition.PerformRenditionActionExecuter;
import org.alfresco.repo.rendition.RenditionServiceImpl;
import org.alfresco.repo.rendition.executer.AbstractRenderingEngine;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
/**
* Thumbnail service implementation unit test
*
* @author Neil McErlean
*/
public class ThumbnailServiceImplParameterTest
{
// Mocked services.
private ActionService mockActionService = mock(ActionService.class);
// Real services - backed by mocked services.
private RenditionServiceImpl renditionService;
private ThumbnailService thumbnailService;
private final NodeRef dummyNodeRef1 = new NodeRef("workspace", "dummy", "dummyID_1");
private final NodeRef dummyNodeRef2 = new NodeRef("workspace", "dummy", "dummyID_2");
private final NodeRef dummyNodeRef3 = new NodeRef("workspace", "dummy", "dummyID_3");
@Before
public void initMockObjects()
{
when(mockActionService.createAction(PerformRenditionActionExecuter.NAME))
.thenReturn(new ActionImpl(dummyNodeRef2, "id", PerformRenditionActionExecuter.NAME, new HashMap<String, Serializable>()));
renditionService = new RenditionServiceImpl()
{
@Override
public RenditionDefinition loadRenditionDefinition(QName renderingActionName)
{
// We're intentionally returning null for this test.
return null;
}
};
renditionService.setActionService(mockActionService);
ThumbnailServiceImpl thumbs = new ThumbnailServiceImpl()
{
@Override
public NodeRef getThumbnailByName(NodeRef node,
QName contentProperty, String thumbnailName)
{
return null;
}
/**
* In this test the thumbnailRef will be null, so we need to ensure
* it is not dereferenced here.
*/
@Override
public NodeRef getThumbnailNode(ChildAssociationRef thumbnailRef)
{
return null;
}
};
thumbs.setRenditionService(renditionService);
thumbnailService = thumbs;
}
/**
* This test method ensures that the parameters on thumbnail-create are
* passed through the RenditionService to the ActionService
*/
@Test
public void createThumbnailPassesParametersToActionService()
{
// As most of the services are mocked out, the actual values used here
// don't matter.
final Map<String, Serializable> parametersUnderTest = new HashMap<String, Serializable>();
parametersUnderTest.put(ImageRenderingEngine.PARAM_RESIZE_WIDTH, new Integer(42));
parametersUnderTest.put(ImageRenderingEngine.PARAM_RESIZE_HEIGHT, new Integer(93));
parametersUnderTest.put(ImageRenderingEngine.PARAM_COMMAND_OPTIONS, "foo");
parametersUnderTest.put(ImageRenderingEngine.PARAM_MAINTAIN_ASPECT_RATIO, Boolean.TRUE);
parametersUnderTest.put(ImageRenderingEngine.PARAM_RESIZE_TO_THUMBNAIL, Boolean.FALSE);
parametersUnderTest.put(AbstractRenderingEngine.PARAM_TARGET_CONTENT_PROPERTY, ContentModel.PROP_CONTENT);
parametersUnderTest.put(RenditionService.PARAM_DESTINATION_NODE, dummyNodeRef2);
ImageTransformationOptions imageTransOpts = new ImageTransformationOptions();
imageTransOpts.setTargetNodeRef(dummyNodeRef2);
imageTransOpts.setTargetContentProperty((QName) parametersUnderTest.get(ImageRenderingEngine.PARAM_TARGET_CONTENT_PROPERTY));
imageTransOpts.setCommandOptions((String) parametersUnderTest.get(ImageRenderingEngine.PARAM_COMMAND_OPTIONS));
ImageResizeOptions resizeOptions = new ImageResizeOptions();
resizeOptions.setHeight((Integer) parametersUnderTest.get(ImageRenderingEngine.PARAM_RESIZE_HEIGHT));
resizeOptions.setWidth((Integer) parametersUnderTest.get(ImageRenderingEngine.PARAM_RESIZE_WIDTH));
resizeOptions.setMaintainAspectRatio((Boolean) parametersUnderTest.get(ImageRenderingEngine.PARAM_MAINTAIN_ASPECT_RATIO));
resizeOptions.setResizeToThumbnail((Boolean) parametersUnderTest.get(ImageRenderingEngine.PARAM_RESIZE_TO_THUMBNAIL));
imageTransOpts.setResizeOptions(resizeOptions);
ThumbnailParentAssociationDetails assocDetails = new ThumbnailParentAssociationDetails(dummyNodeRef3,
ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
"homerSimpson"));
// Now request the creation of the the thumbnail.
thumbnailService.createThumbnail(dummyNodeRef1, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
imageTransOpts, "bartSimpson", assocDetails);
ArgumentCaptor<Action> argument = ArgumentCaptor.forClass(Action.class);
verify(mockActionService).executeAction(argument.capture(), any(NodeRef.class), anyBoolean(), anyBoolean());
final Action performRenditionAction = argument.getValue();
final RenditionDefinition renditionDefn = (RenditionDefinition) performRenditionAction.getParameterValue(PerformRenditionActionExecuter.PARAM_RENDITION_DEFINITION);
Map<String, Serializable> parameters = renditionDefn.getParameterValues();
for (String s : parametersUnderTest.keySet())
{
if (parameters.keySet().contains(s) == false || parameters.get(s) == null || parameters.get(s).toString().length() == 0)
{
fail("Missing parameter " + s);
}
assertEquals("Parameter " + s + " had wrong value.",
parametersUnderTest.get(s), parameters.get(s));
}
}
}

View File

@@ -16,6 +16,7 @@
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.alfresco.repo.thumbnail; package org.alfresco.repo.thumbnail;
import java.io.File; import java.io.File;
@@ -26,6 +27,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest; import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.ContentTransformer;
@@ -33,13 +35,16 @@ import org.alfresco.repo.content.transform.magick.ImageResizeOptions;
import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions;
import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.thumbnail.ThumbnailException; import org.alfresco.service.cmr.thumbnail.ThumbnailException;
import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails;
import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -50,160 +55,189 @@ import org.alfresco.util.BaseAlfrescoSpringTest;
* Thumbnail service implementation unit test * Thumbnail service implementation unit test
* *
* @author Roy Wetherall * @author Roy Wetherall
* @author Neil McErlean
*/ */
public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
{ {
private ThumbnailService thumbnailService; private RenditionService renditionService;
private ThumbnailService thumbnailService;
private ScriptService scriptService; private ScriptService scriptService;
private MimetypeMap mimetypeMap; private MimetypeMap mimetypeMap;
private NodeRef folder; private NodeRef folder;
/** /**
* Called during the transaction setup * Called during the transaction setup
*/ */
@SuppressWarnings("deprecation")
@Override
protected void onSetUpInTransaction() throws Exception protected void onSetUpInTransaction() throws Exception
{ {
super.onSetUpInTransaction(); super.onSetUpInTransaction();
// Get the required services // Get the required services
this.thumbnailService = (ThumbnailService)this.applicationContext.getBean("ThumbnailService"); this.renditionService = (RenditionService) this.applicationContext.getBean("RenditionService");
this.mimetypeMap = (MimetypeMap)this.applicationContext.getBean("mimetypeService"); this.thumbnailService = (ThumbnailService) this.applicationContext.getBean("ThumbnailService");
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService"); this.mimetypeMap = (MimetypeMap) this.applicationContext.getBean("mimetypeService");
this.scriptService = (ScriptService) this.applicationContext.getBean("ScriptService");
// Create a folder and some content // Create a folder and some content
Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>(1); Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>(1);
folderProps.put(ContentModel.PROP_NAME, "testFolder"); folderProps.put(ContentModel.PROP_NAME, "testFolder");
this.folder = this.nodeService.createNode( this.folder = this.nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN,
this.rootNodeRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), ContentModel.TYPE_FOLDER)
ContentModel.ASSOC_CHILDREN, .getChildRef();
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"),
ContentModel.TYPE_FOLDER).getChildRef();
} }
private void checkTransformer() private void checkTransformer()
{ {
ContentTransformer transformer = this.contentService.getImageTransformer(); ContentTransformer transformer = this.contentService.getImageTransformer();
if (transformer == null) assertNotNull("No transformer returned for 'getImageTransformer'", transformer);
{
fail("No transformer returned for 'getImageTransformer'");
}
// Check that it is working // Check that it is working
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
if (!transformer.isTransformable( if (!transformer.isTransformable(MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG,
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions))
MimetypeMap.MIMETYPE_IMAGE_JPEG,
imageTransformationOptions))
{ {
fail("Image transformer is not working. Please check your image conversion command setup."); fail("Image transformer is not working. Please check your image conversion command setup.");
} }
} }
public void testCreateRenditionThumbnailFromImage() throws Exception
{
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
qname.getLocalName());
assertEquals("doclib", details.getName());
assertEquals("image/png", details.getMimetype());
assertEquals("alfresco/thumbnail/thumbnail_placeholder_doclib.png", details.getPlaceHolderResourcePath());
checkTransformer();
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
NodeRef thumbnail0 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib");
assertNotNull(thumbnail0);
checkRenditioned(jpgOrig, "doclib");
checkRendition("doclib", thumbnail0);
outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test");
}
public void testCreateRenditionThumbnailFromPdf() throws Exception
{
QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "doclib");
ThumbnailDefinition details = thumbnailService.getThumbnailRegistry().getThumbnailDefinition(
qname.getLocalName());
assertEquals("doclib", details.getName());
assertEquals("image/png", details.getMimetype());
assertEquals("alfresco/thumbnail/thumbnail_placeholder_doclib.png", details.getPlaceHolderResourcePath());
checkTransformer();
NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, details.getTransformationOptions(), "doclib");
assertNotNull(thumbnail0);
checkRenditioned(pdfOrig, "doclib");
checkRendition("doclib", thumbnail0);
outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test");
}
public void testCreateThumbnailFromImage() throws Exception public void testCreateThumbnailFromImage() throws Exception
{ {
checkTransformer(); checkTransformer();
NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
NodeRef gifOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF); NodeRef gifOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF);
// ===== small: 64x64, marked as thumbnail ==== // ===== small: 64x64, marked as thumbnail ====
ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setWidth(64); imageResizeOptions.setWidth(64);
imageResizeOptions.setHeight(64); imageResizeOptions.setHeight(64);
imageResizeOptions.setResizeToThumbnail(true); imageResizeOptions.setResizeToThumbnail(true);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions); imageTransformationOptions.setResizeOptions(imageResizeOptions);
//ThumbnailDetails createOptions = new ThumbnailDetails(); // ThumbnailDetails createOptions = new ThumbnailDetails();
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
imageTransformationOptions,
"small");
assertNotNull(thumbnail1); assertNotNull(thumbnail1);
checkThumbnailed(jpgOrig, "small"); checkRenditioned(jpgOrig, "small");
checkThumbnail("small", thumbnail1); checkRendition("small", thumbnail1);
outputThumbnailTempContentLocation(thumbnail1, "jpg", "small - 64x64, marked as thumbnail"); outputThumbnailTempContentLocation(thumbnail1, "jpg", "small - 64x64, marked as thumbnail");
// ===== small2: 64x64, aspect not maintained ==== // ===== small2: 64x64, aspect not maintained ====
ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions2 = new ImageResizeOptions();
imageResizeOptions2.setWidth(64); imageResizeOptions2.setWidth(64);
imageResizeOptions2.setHeight(64); imageResizeOptions2.setHeight(64);
imageResizeOptions2.setMaintainAspectRatio(false); imageResizeOptions2.setMaintainAspectRatio(false);
ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions2 = new ImageTransformationOptions();
imageTransformationOptions2.setResizeOptions(imageResizeOptions2); imageTransformationOptions2.setResizeOptions(imageResizeOptions2);
//ThumbnailDetails createOptions2 = new ThumbnailDetails(); // ThumbnailDetails createOptions2 = new ThumbnailDetails();
NodeRef thumbnail2 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail2 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions2, "small2");
imageTransformationOptions2, checkRenditioned(jpgOrig, "small2");
"small2"); checkRendition("small2", thumbnail2);
checkThumbnailed(jpgOrig, "small2");
checkThumbnail("small2", thumbnail2);
outputThumbnailTempContentLocation(thumbnail2, "jpg", "small2 - 64x64, aspect not maintained"); outputThumbnailTempContentLocation(thumbnail2, "jpg", "small2 - 64x64, aspect not maintained");
// ===== half: 50%x50 ===== // ===== half: 50%x50 =====
ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions3 = new ImageResizeOptions();
imageResizeOptions3.setWidth(50); imageResizeOptions3.setWidth(50);
imageResizeOptions3.setHeight(50); imageResizeOptions3.setHeight(50);
imageResizeOptions3.setPercentResize(true); imageResizeOptions3.setPercentResize(true);
ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions3 = new ImageTransformationOptions();
imageTransformationOptions3.setResizeOptions(imageResizeOptions3); imageTransformationOptions3.setResizeOptions(imageResizeOptions3);
// ThumbnailDetails createOptions3 = new ThumbnailDetails(); // ThumbnailDetails createOptions3 = new ThumbnailDetails();
NodeRef thumbnail3 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail3 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions3, "half");
imageTransformationOptions3, checkRenditioned(jpgOrig, "half");
"half"); checkRendition("half", thumbnail3);
checkThumbnailed(jpgOrig, "half");
checkThumbnail("half", thumbnail3);
outputThumbnailTempContentLocation(thumbnail3, "jpg", "half - 50%x50%"); outputThumbnailTempContentLocation(thumbnail3, "jpg", "half - 50%x50%");
// ===== half2: 50%x50 from gif =====
// ===== half2: 50%x50 from gif =====
ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions4 = new ImageResizeOptions();
imageResizeOptions4.setWidth(50); imageResizeOptions4.setWidth(50);
imageResizeOptions4.setHeight(50); imageResizeOptions4.setHeight(50);
imageResizeOptions4.setPercentResize(true); imageResizeOptions4.setPercentResize(true);
ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions4 = new ImageTransformationOptions();
imageTransformationOptions4.setResizeOptions(imageResizeOptions4); imageTransformationOptions4.setResizeOptions(imageResizeOptions4);
// ThumbnailDetails createOptions4 = new ThumbnailDetails(); // ThumbnailDetails createOptions4 = new ThumbnailDetails();
NodeRef thumbnail4 = this.thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail4 = this.thumbnailService.createThumbnail(gifOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions4, "half2");
imageTransformationOptions4, checkRenditioned(gifOrig, "half2");
"half2"); checkRendition("half2", thumbnail4);
checkThumbnailed(gifOrig, "half2");
checkThumbnail("half2", thumbnail4);
outputThumbnailTempContentLocation(thumbnail4, "jpg", "half2 - 50%x50%, from gif"); outputThumbnailTempContentLocation(thumbnail4, "jpg", "half2 - 50%x50%, from gif");
} }
public void testDuplicationNames() public void testDuplicationNames() throws Exception
throws Exception
{ {
checkTransformer(); checkTransformer();
NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setWidth(64); imageResizeOptions.setWidth(64);
imageResizeOptions.setHeight(64); imageResizeOptions.setHeight(64);
imageResizeOptions.setResizeToThumbnail(true); imageResizeOptions.setResizeToThumbnail(true);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions); imageTransformationOptions.setResizeOptions(imageResizeOptions);
//ThumbnailDetails createOptions = new ThumbnailDetails(); // ThumbnailDetails createOptions = new ThumbnailDetails();
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
imageTransformationOptions,
"small");
assertNotNull(thumbnail1); assertNotNull(thumbnail1);
checkThumbnailed(jpgOrig, "small"); checkRenditioned(jpgOrig, "small");
checkThumbnail("small", thumbnail1); checkRendition("small", thumbnail1);
try try
{ {
this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
imageTransformationOptions,
"small");
fail("A duplicate exception should have been raised"); fail("A duplicate exception should have been raised");
} }
catch (ThumbnailException exception) catch (ThumbnailException exception)
@@ -211,180 +245,182 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
// OK since this should have been thrown // OK since this should have been thrown
} }
} }
public void testThumbnailUpdate() public void testThumbnailUpdate() throws Exception
throws Exception
{ {
checkTransformer(); checkTransformer();
// First create a thumbnail // First create a thumbnail
NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setWidth(64); imageResizeOptions.setWidth(64);
imageResizeOptions.setHeight(64); imageResizeOptions.setHeight(64);
imageResizeOptions.setResizeToThumbnail(true); imageResizeOptions.setResizeToThumbnail(true);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions); imageTransformationOptions.setResizeOptions(imageResizeOptions);
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
imageTransformationOptions,
"small");
// Update the thumbnail // Update the thumbnail
this.thumbnailService.updateThumbnail(thumbnail1, imageTransformationOptions); this.thumbnailService.updateThumbnail(thumbnail1, imageTransformationOptions);
} }
public void testGetThumbnailByName() public void testGetThumbnailByName() throws Exception
throws Exception
{ {
checkTransformer(); checkTransformer();
NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
// Check for missing thumbnail // Check for missing thumbnail
NodeRef result1 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small"); NodeRef result1 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small");
assertNull("The thumbnail 'small' should have been missing", result1); assertNull("The thumbnail 'small' should have been missing", result1);
// Create the thumbnail // Create the thumbnail
ImageResizeOptions imageResizeOptions = new ImageResizeOptions(); ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setWidth(64); imageResizeOptions.setWidth(64);
imageResizeOptions.setHeight(64); imageResizeOptions.setHeight(64);
imageResizeOptions.setResizeToThumbnail(true); imageResizeOptions.setResizeToThumbnail(true);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions(); ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions); imageTransformationOptions.setResizeOptions(imageResizeOptions);
this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, MimetypeMap.MIMETYPE_IMAGE_JPEG,
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small");
imageTransformationOptions,
"small");
// Try and retrieve the thumbnail // Try and retrieve the thumbnail
NodeRef result2 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small"); NodeRef result2 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "small");
assertNotNull(result2); assertNotNull(result2);
checkThumbnail("small", result2); checkRendition("small", result2);
// Check for an other thumbnail that doesn't exist // Check for an other thumbnail that doesn't exist
NodeRef result3 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "anotherone"); NodeRef result3 = this.thumbnailService.getThumbnailByName(jpgOrig, ContentModel.PROP_CONTENT, "anotherone");
assertNull("The thumbnail 'anotherone' should have been missing", result3); assertNull("The thumbnail 'anotherone' should have been missing", result3);
} }
// TODO test getThumbnails private void checkRenditioned(NodeRef thumbnailed, String assocName)
private void checkThumbnailed(NodeRef thumbnailed, String assocName)
{ {
assertTrue("Thumbnailed aspect should have been applied", this.nodeService.hasAspect(thumbnailed, ContentModel.ASPECT_THUMBNAILED)); assertTrue("Renditioned aspect should have been applied", this.nodeService.hasAspect(thumbnailed,
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(thumbnailed, RegexQNamePattern.MATCH_ALL, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName)); RenditionModel.ASPECT_RENDITIONED));
assertNotNull(assocs); if (assocName != null)
assertEquals(1, assocs.size()); {
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(thumbnailed, RegexQNamePattern.MATCH_ALL,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName));
assertNotNull(assocs);
assertEquals(1, assocs.size());
}
} }
private void checkThumbnail(String thumbnailName, NodeRef thumbnail) private void checkRendition(String thumbnailName, NodeRef thumbnail)
{ {
// Check the thumbnail is of the correct type // Check the thumbnail is of the correct type
assertEquals(ContentModel.TYPE_THUMBNAIL, this.nodeService.getType(thumbnail)); assertTrue("Thumbnail should have been a rendition",
renditionService.isRendition(thumbnail));
// Check the name
assertEquals(thumbnailName, this.nodeService.getProperty(thumbnail, ContentModel.PROP_THUMBNAIL_NAME)); // Check the name
if (thumbnailName != null)
// Check the contet property value {
assertEquals(ContentModel.PROP_CONTENT, this.nodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT_PROPERTY_NAME)); assertEquals(thumbnailName, this.nodeService.getProperty(thumbnail, ContentModel.PROP_NAME));
}
// Check the content property value
assertEquals(ContentModel.PROP_CONTENT, this.nodeService.getProperty(thumbnail,
ContentModel.PROP_CONTENT_PROPERTY_NAME));
} }
private void outputThumbnailTempContentLocation(NodeRef thumbnail, String ext, String message) private void outputThumbnailTempContentLocation(NodeRef thumbnail, String ext, String message) throws IOException
throws IOException
{ {
File tempFile = File.createTempFile("thumbnailServiceImpTest", "." + ext); File tempFile = File.createTempFile("thumbnailServiceImpTest", "." + ext);
ContentReader reader = this.contentService.getReader(thumbnail, ContentModel.PROP_CONTENT); ContentReader reader = this.contentService.getReader(thumbnail, ContentModel.PROP_CONTENT);
reader.getContent(tempFile); reader.getContent(tempFile);
System.out.println(message + ": " + tempFile.getPath()); System.out.println(message + ": " + tempFile.getPath());
} }
private NodeRef createOrigionalContent(NodeRef folder, String mimetype) /**
throws IOException * This method creates a node under the specified folder whose content is
* taken from the quick file corresponding to the specified MIME type.
*
* @param parentFolder
* @param mimetype
* @return
* @throws IOException
*/
private NodeRef createOriginalContent(NodeRef parentFolder, String mimetype) throws IOException
{ {
String ext = this.mimetypeMap.getExtension(mimetype); String ext = this.mimetypeMap.getExtension(mimetype);
File origFile = AbstractContentTransformerTest.loadQuickTestFile(ext); File origFile = AbstractContentTransformerTest.loadQuickTestFile(ext);
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1); Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "origional." + ext); props.put(ContentModel.PROP_NAME, "origional." + ext);
NodeRef node = this.nodeService.createNode( NodeRef node = this.nodeService.createNode(parentFolder, ContentModel.ASSOC_CONTAINS,
folder, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "original." + ext),
ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT, props).getChildRef();
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "origional." + ext),
ContentModel.TYPE_CONTENT,
props).getChildRef();
ContentWriter writer = this.contentService.getWriter(node, ContentModel.PROP_CONTENT, true); ContentWriter writer = this.contentService.getWriter(node, ContentModel.PROP_CONTENT, true);
writer.setMimetype(mimetype); writer.setMimetype(mimetype);
writer.setEncoding("UTF-8"); writer.setEncoding("UTF-8");
writer.putContent(origFile); writer.putContent(origFile);
return node; return node;
} }
@SuppressWarnings("deprecation")
public void testAutoUpdate() throws Exception public void testAutoUpdate() throws Exception
{ {
checkTransformer(); checkTransformer();
final NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); final NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
ThumbnailDefinition details = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("medium"); ThumbnailDefinition details = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("medium");
@SuppressWarnings("unused") @SuppressWarnings("unused")
final NodeRef thumbnail = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, details.getMimetype(), details.getTransformationOptions(), details.getName()); final NodeRef thumbnail = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT, details
.getMimetype(), details.getTransformationOptions(), details.getName());
setComplete(); setComplete();
endTransaction(); endTransaction();
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>() transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{ {
public Object execute() throws Exception public Object execute() throws Exception
{ {
String ext = ThumbnailServiceImplTest.this.mimetypeMap.getExtension(MimetypeMap.MIMETYPE_IMAGE_JPEG); String ext = ThumbnailServiceImplTest.this.mimetypeMap.getExtension(MimetypeMap.MIMETYPE_IMAGE_JPEG);
File origFile = AbstractContentTransformerTest.loadQuickTestFile(ext); File origFile = AbstractContentTransformerTest.loadQuickTestFile(ext);
ContentWriter writer = ThumbnailServiceImplTest.this.contentService.getWriter(jpgOrig, ContentModel.PROP_CONTENT, true); ContentWriter writer = ThumbnailServiceImplTest.this.contentService.getWriter(jpgOrig,
ContentModel.PROP_CONTENT, true);
writer.putContent(origFile); writer.putContent(origFile);
return null; return null;
} }
}); });
// TODO // TODO
// this test should wait for the async action to run .. will need to commit transaction for that thou! // this test should wait for the async action to run .. will need to
// commit transaction for that thou!
//Thread.sleep(1000);
// Thread.sleep(1000);
} }
public void testHTMLToImageAndSWF() throws Exception public void testHTMLToImageAndSWF() throws Exception
{ {
NodeRef nodeRef = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_HTML); NodeRef nodeRef = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_HTML);
ThumbnailDefinition def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("medium"); ThumbnailDefinition def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("medium");
if (this.contentService.getTransformer(MimetypeMap.MIMETYPE_HTML, def.getMimetype(), def.getTransformationOptions()) != null) ContentTransformer transformer = this.contentService.getTransformer(MimetypeMap.MIMETYPE_HTML, def
{ .getMimetype(), def.getTransformationOptions());
NodeRef thumb = this.thumbnailService.createThumbnail( if (transformer != null)
nodeRef, {
ContentModel.PROP_CONTENT, NodeRef thumb = this.thumbnailService.createThumbnail(nodeRef, ContentModel.PROP_CONTENT,
def.getMimetype(), def.getMimetype(), def.getTransformationOptions(), def.getName());
def.getTransformationOptions(),
def.getName());
assertNotNull(thumb); assertNotNull(thumb);
ContentReader reader = this.contentService.getReader(thumb, ContentModel.PROP_CONTENT); ContentReader reader = this.contentService.getReader(thumb, ContentModel.PROP_CONTENT);
assertNotNull(reader); assertNotNull(reader);
assertEquals(def.getMimetype(), reader.getMimetype()); assertEquals(def.getMimetype(), reader.getMimetype());
assertTrue(reader.getSize() != 0); assertTrue(reader.getSize() != 0);
} }
def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("webpreview"); def = this.thumbnailService.getThumbnailRegistry().getThumbnailDefinition("webpreview");
if (this.contentService.getTransformer(MimetypeMap.MIMETYPE_HTML, def.getMimetype(), def.getTransformationOptions()) != null) if (transformer != null)
{ {
NodeRef thumb = this.thumbnailService.createThumbnail( NodeRef thumb = this.thumbnailService.createThumbnail(nodeRef, ContentModel.PROP_CONTENT,
nodeRef, def.getMimetype(), def.getTransformationOptions(), def.getName());
ContentModel.PROP_CONTENT,
def.getMimetype(),
def.getTransformationOptions(),
def.getName());
assertNotNull(thumb); assertNotNull(thumb);
ContentReader reader = this.contentService.getReader(thumb, ContentModel.PROP_CONTENT); ContentReader reader = this.contentService.getReader(thumb, ContentModel.PROP_CONTENT);
assertNotNull(reader); assertNotNull(reader);
@@ -393,34 +429,111 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest
} }
} }
public void testThumbnailServiceCreateApi() throws Exception
{
// Create a second folder
Map<QName, Serializable> folderProps = new HashMap<QName, Serializable>();
folderProps.put(ContentModel.PROP_NAME, "otherTestFolder");
NodeRef otherFolder = this.nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "otherTestFolder"), ContentModel.TYPE_FOLDER)
.getChildRef();
checkTransformer();
NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
ImageResizeOptions imageResizeOptions = new ImageResizeOptions();
imageResizeOptions.setWidth(64);
imageResizeOptions.setHeight(64);
imageResizeOptions.setResizeToThumbnail(true);
ImageTransformationOptions imageTransformationOptions = new ImageTransformationOptions();
imageTransformationOptions.setResizeOptions(imageResizeOptions);
// Create thumbnail - same MIME type
NodeRef thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "smallJpeg");
assertNotNull(thumbnail1);
checkRenditioned(jpgOrig, "smallJpeg");
checkRendition("smallJpeg", thumbnail1);
outputThumbnailTempContentLocation(thumbnail1, "jpg", "smallJpeg - 64x64, marked as thumbnail");
// Create thumbnail - different MIME type
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallPng");
assertNotNull(thumbnail1);
checkRenditioned(jpgOrig, "smallPng");
checkRendition("smallPng", thumbnail1);
outputThumbnailTempContentLocation(thumbnail1, "png", "smallPng - 64x64, marked as thumbnail");
// Create thumbnail - different content property
// TODO
// Create thumbnail - different command options
// We'll pass illegal command options to ImageMagick in order to trigger an exception
Exception x = null;
try
{
imageTransformationOptions.setCommandOptions("-noSuchOption");
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "smallCO");
} catch (ContentIOException ciox)
{
x = ciox;
ciox.printStackTrace();
}
assertNotNull("Expected exception from ImageMagick due to invalid option", x);
// Reset the command options
imageTransformationOptions.setCommandOptions("");
// Create thumbnail - different target assoc details
ThumbnailParentAssociationDetails tpad
= new ThumbnailParentAssociationDetails(otherFolder,
QName.createQName(NamespaceService.RENDITION_MODEL_1_0_URI, "foo"),
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "bar"));
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, "targetDetails", tpad);
assertNotNull(thumbnail1);
checkRenditioned(jpgOrig, "targetDetails");
checkRendition("targetDetails", thumbnail1);
outputThumbnailTempContentLocation(thumbnail1, "png", "targetDetails - 64x64, marked as thumbnail");
// Create thumbnail - null thumbnail name
thumbnail1 = this.thumbnailService.createThumbnail(jpgOrig, ContentModel.PROP_CONTENT,
MimetypeMap.MIMETYPE_IMAGE_PNG, imageTransformationOptions, null);
assertNotNull(thumbnail1);
checkRenditioned(jpgOrig, null);
checkRendition(null, thumbnail1);
outputThumbnailTempContentLocation(thumbnail1, "png", "'null' - 64x64, marked as thumbnail");
}
public void testRegistry() public void testRegistry()
{ {
ThumbnailRegistry thumbnailRegistry = this.thumbnailService.getThumbnailRegistry(); ThumbnailRegistry thumbnailRegistry = this.thumbnailService.getThumbnailRegistry();
List<ThumbnailDefinition> defs = thumbnailRegistry.getThumnailDefintions(MimetypeMap.MIMETYPE_HTML); List<ThumbnailDefinition> defs = thumbnailRegistry.getThumbnailDefinitions(MimetypeMap.MIMETYPE_HTML);
System.out.println("Definitions ..."); System.out.println("Definitions ...");
for (ThumbnailDefinition def : defs) for (ThumbnailDefinition def : defs)
{ {
System.out.println("Thumbnail Available: " + def.getName()); System.out.println("Thumbnail Available: " + def.getName());
} }
} }
// == Test the JavaScript API == // == Test the JavaScript API ==
public void testJSAPI() throws Exception public void testJSAPI() throws Exception
{ {
NodeRef jpgOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG); NodeRef jpgOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_JPEG);
NodeRef gifOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF); NodeRef gifOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_IMAGE_GIF);
NodeRef pdfOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_PDF); NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF);
NodeRef docOrig = createOrigionalContent(this.folder, MimetypeMap.MIMETYPE_WORD); NodeRef docOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_WORD);
Map<String, Object> model = new HashMap<String, Object>(2); Map<String, Object> model = new HashMap<String, Object>(2);
model.put("jpgOrig", jpgOrig); model.put("jpgOrig", jpgOrig);
model.put("gifOrig", gifOrig); model.put("gifOrig", gifOrig);
model.put("pdfOrig", pdfOrig); model.put("pdfOrig", pdfOrig);
model.put("docOrig", docOrig); model.put("docOrig", docOrig);
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js"); ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js");
this.scriptService.executeScript(location, model); this.scriptService.executeScript(location, model);
} }

View File

@@ -1,293 +0,0 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.thumbnail;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
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.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
/**
* Thumbnailed aspect behaviour bean
*
* @author Roy Wetherall
*/
public class ThumbnailedAspect implements NodeServicePolicies.OnUpdatePropertiesPolicy,
CopyServicePolicies.OnCopyNodePolicy
{
/** Services */
private PolicyComponent policyComponent;
private ThumbnailService thumbnailService;
private ActionService actionService;
private NodeService nodeService;
private DictionaryService dictionaryService;
/**
* Set the policy component
*
* @param policyComponent policy component
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Set the action service
*
* @param actionService action service
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Set the node service
*
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Set the thumbnail service
*
* @param thumbnailService thumbnail service
*/
public void setThumbnailService(ThumbnailService thumbnailService)
{
this.thumbnailService = thumbnailService;
}
/**
* Set the dictionary service
*
* @param dictionaryService dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* Initialise method
*/
public void init()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
ContentModel.ASPECT_THUMBNAILED,
new JavaBehaviour(this, "onUpdateProperties", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
ContentModel.ASPECT_THUMBNAILED,
new JavaBehaviour(this, "getCopyCallback"));
}
/**
* @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<QName, Serializable> before,
Map<QName, Serializable> after)
{
// Ignore working copies
if (this.nodeService.exists(nodeRef) == true &&
this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == false)
{
// check if any of the content properties have changed
for (QName propertyQName : after.keySet())
{
// is this a content property?
PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
if (propertyDef == null)
{
// the property is not recognised
continue;
}
if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
{
// not a content type
continue;
}
try
{
ContentData beforeValue = (ContentData) before.get(propertyQName);
ContentData afterValue = (ContentData) after.get(propertyQName);
// Figure out if the content is new or not
boolean newContent = false;
String beforeContentUrl = null;
if (beforeValue != null)
{
beforeContentUrl = beforeValue.getContentUrl();
}
String afterContentUrl = null;
if (afterValue != null)
{
afterContentUrl = afterValue.getContentUrl();
}
if (beforeContentUrl == null && afterContentUrl != null)
{
newContent = true;
}
if (afterValue != null && afterValue.getContentUrl() == null)
{
// no URL - ignore
}
else if (newContent == false && EqualsHelper.nullSafeEquals(beforeValue, afterValue) == false)
{
// Queue the update
queueUpdate(nodeRef, propertyQName);
}
}
catch (ClassCastException e)
{
// properties don't conform to model
continue;
}
}
}
}
/**
* Queue the update to happen asynchronously
*
* @param nodeRef node reference
* @param contentProperty content property
*/
private void queueUpdate(NodeRef nodeRef, QName contentProperty)
{
Boolean automaticUpdate = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_AUTOMATIC_UPDATE);
if (automaticUpdate != null && automaticUpdate.booleanValue() == true)
{
CompositeAction compositeAction = actionService.createCompositeAction();
List<NodeRef> thumbnails = this.thumbnailService.getThumbnails(nodeRef, contentProperty, null, null);
for (NodeRef thumbnail : thumbnails)
{
// Execute the update thumbnail action async for each thumbnail
Action action = actionService.createAction(UpdateThumbnailActionExecuter.NAME);
action.setParameterValue(UpdateThumbnailActionExecuter.PARAM_CONTENT_PROPERTY, contentProperty);
action.setParameterValue(UpdateThumbnailActionExecuter.PARAM_THUMBNAIL_NODE, thumbnail);
compositeAction.addAction(action);
}
actionService.executeAction(compositeAction, nodeRef, false, true);
}
}
/**
* @return Returns {@link ThumbnailedAspectCopyBehaviourCallback}
*/
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
{
return ThumbnailedAspectCopyBehaviourCallback.INSTANCE;
}
/**
* Behaviour for the {@link ContentModel#ASPECT_THUMBNAILED <b>cm:thumbnailed</b>} aspect.
*
* @author Derek Hulley
* @since 3.2
*/
private static class ThumbnailedAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
{
private static final CopyBehaviourCallback INSTANCE = new ThumbnailedAspectCopyBehaviourCallback();
/**
* @return Returns <tt>true</tt> always
*/
@Override
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
{
return true;
}
/**
* Copy thumbnail-related associations, {@link ContentModel#ASSOC_THUMBNAILS} regardless of
* cascade options.
*/
@Override
public ChildAssocCopyAction getChildAssociationCopyAction(
QName classQName,
CopyDetails copyDetails,
CopyChildAssociationDetails childAssocCopyDetails)
{
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
if (childAssocRef.getTypeQName().equals(ContentModel.ASSOC_THUMBNAILS))
{
return ChildAssocCopyAction.COPY_CHILD;
}
else
{
throw new IllegalStateException(
"Behaviour should have been invoked: \n" +
" Aspect: " + this.getClass().getName() + "\n" +
" " + childAssocCopyDetails + "\n" +
" " + copyDetails);
}
}
/**
* Copy only the {@link ContentModel#PROP_AUTOMATIC_UPDATE}
*/
@Override
public Map<QName, Serializable> getCopyProperties(
QName classQName,
CopyDetails copyDetails,
Map<QName, Serializable> properties)
{
Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>(5);
Serializable value = properties.get(ContentModel.PROP_AUTOMATIC_UPDATE);
newProperties.put(ContentModel.PROP_AUTOMATIC_UPDATE, value);
return newProperties;
}
}
}

View File

@@ -27,20 +27,31 @@ import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
* Update thumbnail action executer. * Update thumbnail action executer.
* *
* NOTE: This action is used to facilitate the async update of thumbnails. It is not intended for genereral usage. * NOTE: This action is used to facilitate the async update of thumbnails. It is not intended for general usage.
* *
* @author Roy Wetherall * @author Roy Wetherall
* @author Neil McErlean
*/ */
public class UpdateThumbnailActionExecuter extends ActionExecuterAbstractBase public class UpdateThumbnailActionExecuter extends ActionExecuterAbstractBase
{ {
/** Logger */
private static Log logger = LogFactory.getLog(UpdateThumbnailActionExecuter.class);
/** Rendition Service */
private RenditionService renditionService;
/** Thumbnail Service */ /** Thumbnail Service */
private ThumbnailService thumbnailService; private ThumbnailService thumbnailService;
@@ -52,6 +63,16 @@ public class UpdateThumbnailActionExecuter extends ActionExecuterAbstractBase
public static final String PARAM_CONTENT_PROPERTY = "content-property"; public static final String PARAM_CONTENT_PROPERTY = "content-property";
public static final String PARAM_THUMBNAIL_NODE = "thumbnail-node"; public static final String PARAM_THUMBNAIL_NODE = "thumbnail-node";
/**
* Injects the rendition service.
*
* @param renditionService the rendition service.
*/
public void setRenditionService(RenditionService renditionService)
{
this.renditionService = renditionService;
}
/** /**
* Set the thumbnail service * Set the thumbnail service
* *
@@ -86,17 +107,17 @@ public class UpdateThumbnailActionExecuter extends ActionExecuterAbstractBase
} }
if (this.nodeService.exists(thumbnailNodeRef) == true && if (this.nodeService.exists(thumbnailNodeRef) == true &&
ContentModel.TYPE_THUMBNAIL.equals(this.nodeService.getType(thumbnailNodeRef)) == true) renditionService.isRendition(thumbnailNodeRef))
{ {
// Get the thumbnail Name // Get the thumbnail Name
String thumbnailName = (String)this.nodeService.getProperty(thumbnailNodeRef, ContentModel.PROP_THUMBNAIL_NAME); ChildAssociationRef parent = renditionService.getSourceNode(thumbnailNodeRef);
String thumbnailName = parent.getQName().getLocalName();
// Get the details of the thumbnail // Get the details of the thumbnail
ThumbnailRegistry registry = this.thumbnailService.getThumbnailRegistry(); ThumbnailRegistry registry = this.thumbnailService.getThumbnailRegistry();
ThumbnailDefinition details = registry.getThumbnailDefinition(thumbnailName); ThumbnailDefinition details = registry.getThumbnailDefinition(thumbnailName);
if (details == null) if (details == null)
{ {
// Throw exception
throw new AlfrescoRuntimeException("The thumbnail name '" + thumbnailName + "' is not registered"); throw new AlfrescoRuntimeException("The thumbnail name '" + thumbnailName + "' is not registered");
} }
@@ -121,5 +142,4 @@ public class UpdateThumbnailActionExecuter extends ActionExecuterAbstractBase
paramList.add(new ParameterDefinitionImpl(PARAM_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false, getParamDisplayLabel(PARAM_CONTENT_PROPERTY))); paramList.add(new ParameterDefinitionImpl(PARAM_CONTENT_PROPERTY, DataTypeDefinition.QNAME, false, getParamDisplayLabel(PARAM_CONTENT_PROPERTY)));
paramList.add(new ParameterDefinitionImpl(PARAM_THUMBNAIL_NODE, DataTypeDefinition.QNAME, false, getParamDisplayLabel(PARAM_THUMBNAIL_NODE))); paramList.add(new ParameterDefinitionImpl(PARAM_THUMBNAIL_NODE, DataTypeDefinition.QNAME, false, getParamDisplayLabel(PARAM_THUMBNAIL_NODE)));
} }
} }

View File

@@ -18,20 +18,31 @@
*/ */
package org.alfresco.repo.thumbnail.script; package org.alfresco.repo.thumbnail.script;
import org.alfresco.model.ContentModel; import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.jscript.ScriptNode; import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.thumbnail.ThumbnailDefinition; import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
/** /**
* @author Roy Wetherall * @author Roy Wetherall
* @author Neil McErlean
*/ */
public class ScriptThumbnail extends ScriptNode public class ScriptThumbnail extends ScriptNode
{ {
private static final long serialVersionUID = 7854749986083635678L; private static final long serialVersionUID = 7854749986083635678L;
/** Logger */
private static Log logger = LogFactory.getLog(ScriptThumbnail.class);
/** /**
* Constructor * Constructor
* *
@@ -49,7 +60,25 @@ public class ScriptThumbnail extends ScriptNode
*/ */
public void update() public void update()
{ {
String name = (String)services.getNodeService().getProperty(nodeRef, ContentModel.PROP_THUMBNAIL_NAME); List<ChildAssociationRef> parentRefs = services.getNodeService().getParentAssocs(nodeRef, RenditionModel.ASSOC_RENDITION, RegexQNamePattern.MATCH_ALL);
// There should in fact only ever be one parent association of type rendition on any rendition node.
if (parentRefs.size() != 1)
{
StringBuilder msg = new StringBuilder();
msg.append("Node ")
.append(nodeRef)
.append(" has ")
.append(parentRefs.size())
.append(" rendition parents. Unable to update.");
if (logger.isWarnEnabled())
{
logger.warn(msg.toString());
}
throw new AlfrescoRuntimeException(msg.toString());
}
String name = parentRefs.get(0).getQName().getLocalName();
ThumbnailDefinition def = services.getThumbnailService().getThumbnailRegistry().getThumbnailDefinition(name); ThumbnailDefinition def = services.getThumbnailService().getThumbnailRegistry().getThumbnailDefinition(name);
services.getThumbnailService().updateThumbnail(this.nodeRef, def.getTransformationOptions()); services.getThumbnailService().updateThumbnail(this.nodeRef, def.getTransformationOptions());
} }

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2005-2010 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.action;
import java.io.Serializable;
import java.util.List;
/**
* @author Nick Smith
*/
public interface ActionList<A extends Action> extends Serializable
{
/**
* Indicates whether there are any actions
*
* @return true if there are actions, false otherwise
*/
boolean hasActions();
/**
* Add an action to the end of the list
*
* @param action the action
*/
void addAction(A action);
/**
* Add an action to the list at the index specified
*
* @param index the index
* @param action the action
*/
void addAction(int index, A action);
/**
* Replace the action at the specfied index with the passed action.
*
* @param index the index
* @param action the action
*/
void setAction(int index, A action);
/**
* Gets the index of an action
*
* @param action the action
* @return the index
*/
int indexOfAction(A action);
/**
* Get list containing the actions in their current order
*
* @return the list of actions
*/
List<A> getActions();
/**
* Get an action at a given index
*
* @param index the index
* @return the action
*/
A getAction(int index);
/**
* Remove an action from the list
*
* @param action the action
*/
void removeAction(A action);
/**
* Remove all actions from the list
*/
void removeAllActions();
}

View File

@@ -16,79 +16,15 @@
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.alfresco.service.cmr.action;
import java.util.List; package org.alfresco.service.cmr.action;
/** /**
* Composite action * Composite action
* *
* @author Roy Wetherall * @author Roy Wetherall
*/ */
public interface CompositeAction extends Action public interface CompositeAction extends Action, ActionList<Action>
{ {
/**
* Indicates whether there are any actions
*
* @return true if there are actions, false otherwise
*/
boolean hasActions();
/**
* Add an action to the end of the list
*
* @param action the action
*/
void addAction(Action action);
/**
* Add an action to the list at the index specified
*
* @param index the index
* @param action the action
*/
void addAction(int index, Action action);
/**
* Replace the action at the specfied index with the passed action.
*
* @param index the index
* @param action the action
*/
void setAction(int index, Action action);
/**
* Gets the index of an action
*
* @param action the action
* @return the index
*/
int indexOfAction(Action action);
/**
* Get list containing the actions in their current order
*
* @return the list of actions
*/
List<Action> getActions();
/**
* Get an action at a given index
*
* @param index the index
* @return the action
*/
Action getAction(int index);
/**
* Remove an action from the list
*
* @param action the action
*/
void removeAction(Action action);
/**
* Remove all actions from the list
*/
void removeAllActions();
} }

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import org.alfresco.service.cmr.action.ActionList;
/**
* This is a special {@link RenditionDefinition} which allows sequential
* execution of a list of other {@link RenditionDefinition}s. For example, it
* might be used to transform a PDF file to a JPEG imaged and then resize that
* image. This would be achieved by creating a
* {@link CompositeRenditionDefinition} that has two sub-definitions, one to
* reformat the PDF to a JPEG image and the second to resize the JPEG image.
*
* @author Nick Smith
*/
public interface CompositeRenditionDefinition extends RenditionDefinition, ActionList<RenditionDefinition>
{
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* This interface defines a strategy object used for finding a {@link NodeRef}.
*
* @author Nick Smith
*/
public interface NodeLocator
{
/**
* Finds a {@link NodeRef} given a starting {@link NodeRef} and a
* {@link Map} of parameters.
*
* @param sourceNode the starting point for locating a new node.
* @param params a {@link Map} of parameters.
* @return the {@link NodeRef}.
*/
NodeRef getNode(NodeRef sourceNode, Map<String, Serializable> params);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
/**
* This interface defines a callback object which can be used to handle the ultimate
* result of asynchronous renditions.
*
* @author Neil McErlean
* @see RenditionService#render(org.alfresco.service.cmr.repository.NodeRef, RenditionDefinition, RenderCallback)
*/
public interface RenderCallback
{
/**
* This callback method will be called upon successful completion of an asynchronous
* rendition.
* @param primaryParentOfNewRendition a ChildAssociationRef linking the new rendition
* object to its primary parent.
*/
void handleSuccessfulRendition(ChildAssociationRef primaryParentOfNewRendition);
/**
* This callback method will be called upon unsuccessful completion of an
* asynchronous rendition.
* @param t the Throwable giving the cause of the rendition failure.
*/
void handleFailedRendition(Throwable t);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import org.alfresco.service.cmr.action.ActionDefinition;
/**
* This class describes a rendering engine and defines what parameters can/must
* be specified for that rendering engine.
*
* @author Nick Smith
*/
public interface RenderingEngineDefinition extends ActionDefinition
{
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import java.io.Serializable;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* This class is used to fully specify a type of rendition. It specifies which
* rendering engine will be used as well as the parameters that will be given to
* that engine.
* <P/>
* Every RenditionDefinition has a <code>renditionName</code> attribute which
* uniquely identifies it.
*
* @author Nick Smith
* @author Neil McErlean
*/
public interface RenditionDefinition extends Action, Serializable
{
/**
* @return the name which uniquely identifies this rendering action.
*/
public QName getRenditionName();
/**
* Returns the node to which the rendition is linked when it is first
* created. Typically this location is only temporary temporary as the
* rendition will be moved to a different location by the
* {@link RenditionService} shortly after its creation.
*
* @return the renditionParent
*/
public NodeRef getRenditionParent();
/**
* Sets the node to which the rendition is linked when it is first created.
* Typically this location is only temporary temporary as the rendition will
* be moved to a different location by the {@link RenditionService} shortly
* after its creation.
*
* @param renditionParent the renditionParent to set
*/
public void setRenditionParent(NodeRef renditionParent);
/**
* Returns the association type used to link the rendition to its parent
* node after it has been newly created. Typically this association is only
* temporary as the rendition will be moved to a different location by the
* {@link RenditionService} shortly after its creation.
*
* @return the renditionAssociationType
*/
public QName getRenditionAssociationType();
/**
* Sets the association type used to link the rendition to its parent node
* after it has been newly created. Typically this association is only
* temporary as the rendition will be moved to a different location by the
* {@link RenditionService} shortly after its creation.
*
* @param renditionAssociationType the renditionAssociationType to set
*/
public void setRenditionAssociationType(QName renditionAssociationType);
/**
* This method sets a callback object for use in asynchronous renditions. It is
* this object that will be notified of the successful or unsuccessful completion
* of these renditions.
*
* @param callback a callback object, which may be null.
*/
public void setCallback(RenderCallback callback);
/**
* This method gets the registered callback object for use with asynchronous
* renditions.
*
* @return the callback object
*/
public RenderCallback getCallback();
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import java.util.List;
import org.alfresco.repo.rendition.RenditionDefinitionPersister;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* @author Nick Smith
* @author Neil McErlean
*/
public interface RenditionService extends RenditionDefinitionPersister
{
public static final String PARAM_DESTINATION_NODE = "rendition-destination-node";
public static final String PARAM_DESTINATION_PATH_TEMPLATE = "destination-path-template";
public static final String PARAM_RENDITION_NODETYPE = "rendition-nodetype";
public static final String PARAM_ORPHAN_EXISTING_RENDITION = "orphan-existing-rendition";
/**
* Returns the {@link RenderingEngineDefinition} associated with the
* specified rendering engine name.
*
* @param name The rendering engine name.
* @return The {@link RenderingEngineDefinition} or null.
*/
RenderingEngineDefinition getRenderingEngineDefinition(String name);
/**
* @return A {@link List} of all available {@link RenderingEngineDefinition}
* s.
*/
List<RenderingEngineDefinition> getRenderingEngineDefinitions();
/**
* Creates a new {@link RenditionDefinition} and sets the rendition name and
* the rendering engine name to the specified values.
*
* @param renditionName A unique identifier used to specify the created
* {@link RenditionDefinition}.
* @param renderingEngineName The name of the rendering engine associated
* with this {@link RenditionDefinition}.
* @return the created {@link RenditionDefinition}.
*/
RenditionDefinition createRenditionDefinition(QName renditionName, String renderingEngineName);
/**
* Creates a new {@link CompositeRenditionDefinition} and sets the rendition
* name and the rendering engine name to the specified values.
*
* @param renditionName A unique identifier used to specify the created
* {@link RenditionDefinition}.
* @return the created {@link CompositeRenditionDefinition}.
*/
CompositeRenditionDefinition createCompositeRenditionDefinition(QName renditionName);
/**
* This method gets all the renditions of the specified node.
*
* @return a list of ChildAssociationRefs which link the source node to the
* renditions.
*/
List<ChildAssociationRef> getRenditions(NodeRef node);
/**
* This method gets all the renditions of the specified node filtered by
* MIME-type prefix. Renditions whose MIME-type string startsWith the prefix
* will be returned.
*
* @param node the source node for the renditions
* @param mimeTypePrefix a prefix to check against the rendition MIME-types.
* This must not be null and must not be an empty String
* @return a list of ChildAssociationRefs which link the source node to the
* filtered renditions.
*/
List<ChildAssociationRef> getRenditions(NodeRef node, String mimeTypePrefix);
/**
* This method gets the rendition of the specified node identified by
* the provided rendition name.
*
* @param node the source node for the renditions
* @param renditionName the renditionName used to identify a rendition.
* @return the ChildAssociationRef which links the source node to the
* rendition or <code>null</code> if there is no such rendition.
*/
ChildAssociationRef getRenditionByName(NodeRef node, QName renditionName);
/**
* This method returns <code>true</code> if the specified NodeRef is a valid
* rendition node, else <code>false</code>. A nodeRef is a rendition node
* if it has the rn:rendition aspect (or sub-aspect) applied.
*
* @param node
* @return <code>true</code> if a rendition, else <code>false</code>
*/
boolean isRendition(NodeRef node);
/**
* This method gets the source node for the specified rendition node. There
* should only be one source node for any given rendition node.
*
* @param renditionNode the nodeRef holding the rendition.
* @return the ChildAssociationRef whose parentNodeRef is the source node, or
* <code>null</code> if there is no source node.
*
* @see RenditionService#isRendition(NodeRef)
*/
ChildAssociationRef getSourceNode(NodeRef renditionNode);
//TODO The result should be the link to the primary parent.
/**
* This method synchronously renders content as specified by the given
* {@link RenditionDefinition}. The content to be rendered is provided by
* the specified source node.
*
* @param sourceNode the node from which the content is retrieved.
* @param renditionDefinition this fully specifies the rendition which is to
* be performed.
* @return a child association reference which is the link from the source
* node to the newly rendered content.
* @throws RenditionServiceException if there is a problem in rendering the
* given sourceNode
*/
ChildAssociationRef render(NodeRef sourceNode, RenditionDefinition renditionDefinition);
/**
* This method asynchronously renders content as specified by the given
* {@link RenditionDefinition}. The content to be rendered is provided by
* the specified source node.
*
* @param sourceNode the node from which the content is retrieved.
* @param renditionDefinition this fully specifies the rendition which is to
* be performed.
* @param callback a callback object to handle the ultimate result of the rendition.
*/
void render(NodeRef sourceNode, RenditionDefinition renditionDefinition,
RenderCallback callback);
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.service.cmr.rendition;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Rendition Service Exception Class
*
* @author Neil McErlean
*/
public class RenditionServiceException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = -6947067735970465937L;
/**
* Constructs a Rendition Service Exception with the specified message.
*
* @param message the message string
*/
public RenditionServiceException(String message)
{
super(message);
}
/**
* Constructs a Rendition Service Exception with the specified message and source exception.
*
* @param message the message string
* @param source the source exception
*/
public RenditionServiceException(String message, Throwable source)
{
super(message, source);
}
}

View File

@@ -88,6 +88,9 @@ public interface NamespaceService extends NamespacePrefixResolver
/** Alfresco Forums Prefix */ /** Alfresco Forums Prefix */
static final String FORUMS_MODEL_PREFIX = "fm"; static final String FORUMS_MODEL_PREFIX = "fm";
/** Rendition Model URI */
static final String RENDITION_MODEL_1_0_URI = "http://www.alfresco.org/model/rendition/1.0";
/** Alfresco View Namespace URI */ /** Alfresco View Namespace URI */
static final String REPOSITORY_VIEW_1_0_URI = "http://www.alfresco.org/view/repository/1.0"; static final String REPOSITORY_VIEW_1_0_URI = "http://www.alfresco.org/view/repository/1.0";

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* 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.util;
import java.io.*;
import java.util.LinkedList;
import javax.xml.XMLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;
import org.xml.sax.SAXException;
/**
* FreeMarker utility functions.
*
* @author Ariel Backenroth
*/
public class FreeMarkerUtil
{
public static String buildNamespaceDeclaration(final Document xml)
{
final Element docEl = xml.getDocumentElement();
final NamedNodeMap attributes = docEl.getAttributes();
final StringBuilder result = new StringBuilder();
for (int i = 0; i < attributes.getLength(); i++)
{
final Node a = attributes.item(i);
if (a.getNodeName().startsWith(XMLConstants.XMLNS_ATTRIBUTE))
{
final String prefix = a.getNodeName().substring((XMLConstants.XMLNS_ATTRIBUTE + ":").length());
final String uri = a.getNodeValue();
if (result.length() != 0)
{
result.append(",\n");
}
result.append("\"").append(prefix).append("\":\"").append(uri).append("\"");
}
}
return "<#ftl ns_prefixes={\n" + result.toString() + "}>\n";
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2005-2008 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.util;
import java.io.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
/**
* XML utility functions.
*
* @author Ariel Backenroth
*/
public class XMLUtil
{
private static final Log LOGGER = LogFactory.getLog(XMLUtil.class);
/** utility function for creating a document */
public static Document newDocument()
{
return XMLUtil.getDocumentBuilder().newDocument();
}
/** utility function for serializing a node */
public static void print(final Node n, final Writer output)
{
XMLUtil.print(n, output, true);
}
/** utility function for serializing a node */
public static void print(final Node n, final Writer output, final boolean indent)
{
try
{
final TransformerFactory tf = TransformerFactory.newInstance();
final Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no");
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
t.setOutputProperty(OutputKeys.METHOD, "xml");
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("writing out a document for " +
(n instanceof Document
? ((Document)n).getDocumentElement()
: n).getNodeName() +
" to " + (output instanceof StringWriter
? "string"
: output));
}
t.transform(new DOMSource(n), new StreamResult(output));
}
catch (TransformerException te)
{
te.printStackTrace();
assert false : te.getMessage();
}
}
/** utility function for serializing a node */
public static void print(final Node n, final File output)
throws IOException
{
XMLUtil.print(n, new FileWriter(output));
}
/** utility function for serializing a node */
public static String toString(final Node n)
{
return XMLUtil.toString(n, true);
}
/** utility function for serializing a node */
public static String toString(final Node n, final boolean indent)
{
final StringWriter result = new StringWriter();
XMLUtil.print(n, result, indent);
return result.toString();
}
/** utility function for parsing xml */
public static Document parse(final String source)
throws SAXException,
IOException
{
return XMLUtil.parse(new ByteArrayInputStream(source.getBytes("UTF-8")));
}
/** utility function for parsing xml */
public static Document parse(final NodeRef nodeRef,
final ContentService contentService)
throws SAXException,
IOException
{
final ContentReader contentReader =
contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
final InputStream in = contentReader.getContentInputStream();
return XMLUtil.parse(in);
}
/** utility function for parsing xml */
public static Document parse(final int version,
final String path,
final AVMService avmService)
throws SAXException,
IOException
{
return XMLUtil.parse(avmService.getFileInputStream(version, path));
}
/** utility function for parsing xml */
public static Document parse(final File source)
throws SAXException,
IOException
{
return XMLUtil.parse(new FileInputStream(source));
}
/** utility function for parsing xml */
public static Document parse(final InputStream source)
throws SAXException,
IOException
{
try
{
final DocumentBuilder db = XMLUtil.getDocumentBuilder();
return db.parse(source);
}
finally
{
source.close();
}
}
/** provides a document builder that is namespace aware but not validating by default */
public static DocumentBuilder getDocumentBuilder()
{
return XMLUtil.getDocumentBuilder(true, false);
}
/**
* FOR DIAGNOSTIC PURPOSES ONLY - incomplete<br/>
* Builds a path to the node relative to the to node provided.
* @param from the node from which to build the xpath
* @param to an ancestor of <tt>from</tt> which will be the root of the path
* @return an xpath to <tt>to</tt> rooted at <tt>from</tt>.
*/
public static String buildXPath(final Node from, final Element to)
{
String result = "";
Node tmp = from;
do
{
if (tmp instanceof Attr)
{
assert result.length() == 0;
result = "@" + tmp.getNodeName();
}
else if (tmp instanceof Element)
{
Node tmp2 = tmp;
int position = 1;
while (tmp2.getPreviousSibling() != null)
{
if (tmp2.getNodeName().equals(tmp.getNodeName()))
{
position++;
}
tmp2 = tmp2.getPreviousSibling();
}
String part = tmp.getNodeName() + "[" + position + "]";
result = "/" + part + result;
}
else if (tmp instanceof Text)
{
assert result.length() == 0;
result = "/text()";
}
else
{
if (LOGGER.isDebugEnabled())
{
throw new IllegalArgumentException("unsupported node type " + tmp);
}
}
tmp = tmp.getParentNode();
}
while (tmp != to.getParentNode() && tmp != null);
return result;
}
public static DocumentBuilder getDocumentBuilder(final boolean namespaceAware,
final boolean validating)
{
try
{
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(namespaceAware);
dbf.setValidating(validating);
return dbf.newDocumentBuilder();
}
catch (ParserConfigurationException pce)
{
LOGGER.error(pce);
return null;
}
}
/**
* Provides a NodeList of multiple nodelists
*/
public static NodeList combine(final NodeList... nls)
{
return new NodeList()
{
public Node item(final int index)
{
int offset = 0;
for (int i = 0; i < nls.length; i++)
{
if (index - offset < nls[i].getLength())
{
return nls[i].item(index - offset);
}
else
{
offset += nls[i].getLength();
}
}
return null;
}
public int getLength()
{
int result = 0;
for (int i = 0; i < nls.length; i++)
{
result += nls[i].getLength();
}
return result;
}
};
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2005-2010 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.util.json;
/**
* @author Nick Smith
*/
public abstract class AbstractJsonSerializerBean<T, S> implements JsonSerializer<T, S>
{
private Class<? extends T> classToSerialize;
private AlfrescoJsonSerializer jsonSerializer;
/**
* @param classToSerialize the classToSerialize to set
*/
public void setClassToSerialize(Class<? extends T> classToSerialize)
{
this.classToSerialize = classToSerialize;
}
/**
* @param jsonSerializer the jsonSerializer to set
*/
public void setJsonSerializer(AlfrescoJsonSerializer jsonSerializer)
{
this.jsonSerializer = jsonSerializer;
}
public void init()
{
jsonSerializer.register(classToSerialize, this);
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2005-20010 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.util.json;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.chemistry.tck.atompub.utils.ISO8601DateFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* @author Nick Smith
*/
public class AlfrescoJsonSerializer
{
private final NamespaceService namespaceService;
private final Map<Class<?>, JsonSerializer<?, ?>> serializers = new HashMap<Class<?>, JsonSerializer<?, ?>>();
public AlfrescoJsonSerializer(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
public void register(Class<?> clazz, JsonSerializer<?, ?> serializer)
{
serializers.put(clazz, serializer);
}
public Object getJsonValue(Object value) throws JSONException
{
if (value instanceof Date) { return getJsonDate((Date) value); }
if (value instanceof QName) { return getJsonQName((QName) value); }
if (value instanceof NodeRef)
{
return value.toString();
}
else if (value instanceof Collection<?>)
{
return getJsonArray((Collection<?>) value);
}
else if (value instanceof Map<?, ?>) { return getJsonObject((Map<?, ?>) value); }
return value;
}
private JSONObject getJsonObject(Map<?, ?> map) throws JSONException
{
JSONObject object = new JSONObject();
for (Entry<?, ?> entry : map.entrySet())
{
String key = getJsonKey(entry.getKey());
Object value = getJsonValue(entry.getValue());
object.put(key, value);
}
return object;
}
private String getJsonKey(Object key)
{
if (key instanceof QName) { return getJsonQName((QName) key); }
return key.toString();
}
private String getJsonQName(QName name)
{
String nameString = name.toPrefixString(namespaceService);
return nameString.replaceFirst(":", "_");
}
private JSONObject getJsonDate(Date date) throws JSONException
{
JSONObject isoDate = new JSONObject();
isoDate.put("iso8601", ISO8601DateFormat.format(date));
return isoDate;
}
private JSONArray getJsonArray(Collection<?> values) throws JSONException
{
JSONArray array = new JSONArray();
for (Object val : values)
{
array.put(getJsonValue(val));
}
return array;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2005-2010 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.util.json;
/**
*
* @author Nick Smith
*/
public class DefaultJsonSerializer implements JsonSerializer<Object, Object>
{
/*
* @see org.alfresco.util.json.JsonSerializer#deserialize(java.lang.Object)
*/
public Object deserialize(Object object)
{
return object;
}
/*
* @see org.alfresco.util.json.JsonSerializer#serialize(java.lang.Object)
*/
public Object serialize(Object object)
{
return object;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2005-2010 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.util.json;
/**
* An interface for converting objects of a specified type (T) into a serialized
* Json format (of type S).
*
* @author Nick Smith
*/
public interface JsonSerializer<T, S>
{
S serialize(T object);
T deserialize(S object);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.