Hi,
I have submitted a patch for review:
https://gerrit.libreoffice.org/2148
To pull it, you can do:
git pull ssh://gerrit.libreoffice.org:29418/core refs/changes/48/2148/1
CMIS: made it work with Lotus Live
Making libcmis and LibreOffice work with Lotus Live service needed a few
hacks to either better implement CMIS or workaround some bad
implementations.
As a general improvement, the CheckOut InfoBar isn't shown if the
document can't be checked out.
Change-Id: I7bb4211db0506998cef40ac1fb6375e647234085
---
M libcmis/UnpackedTarball_cmis.mk
A libcmis/libcmis-0.3.0-lotus-live-fix.patch
M sfx2/source/view/sfxbasecontroller.cxx
M ucb/source/ucp/cmis/cmis_content.cxx
M ucb/source/ucp/cmis/cmis_content.hxx
M ucb/source/ucp/cmis/cmis_url.cxx
6 files changed, 259 insertions(+), 35 deletions(-)
diff --git a/libcmis/UnpackedTarball_cmis.mk b/libcmis/UnpackedTarball_cmis.mk
index a25c316..0a9f9f8 100644
--- a/libcmis/UnpackedTarball_cmis.mk
+++ b/libcmis/UnpackedTarball_cmis.mk
@@ -18,6 +18,7 @@
libcmis/libcmis-0.3.0-win.patch \
libcmis/libcmis-0.3.0.patch \
libcmis/libcmis-0.3.0-proxy.patch \
+ libcmis/libcmis-0.3.0-lotus-live-fix.patch \
))
ifeq ($(OS)$(COM),WNTMSC)
diff --git a/libcmis/libcmis-0.3.0-lotus-live-fix.patch b/libcmis/libcmis-0.3.0-lotus-live-fix.patch
new file mode 100644
index 0000000..2aca934
--- /dev/null
+++ b/libcmis/libcmis-0.3.0-lotus-live-fix.patch
@@ -0,0 +1,122 @@
+diff --git src/libcmis/atom-folder.cxx src/libcmis/atom-folder.cxx
+index 68fb124..2756a5d 100644
+--- src/libcmis/atom-folder.cxx
++++ src/libcmis/atom-folder.cxx
+@@ -57,8 +57,11 @@ vector< libcmis::ObjectPtr > AtomFolder::getChildren( ) throw ( libcmis::Excepti
+ {
+ AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" );
+
++ // Some servers aren't giving the GetChildren properly... if not defined, we need to try
++ // as we may have the right to proceed.
+ if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() &&
+- !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) ) )
++ ( !getAllowableActions()->isAllowed( libcmis::ObjectAction::GetChildren ) &&
++ getAllowableActions()->isDefined( libcmis::ObjectAction::GetChildren ) ) ) )
+ throw libcmis::Exception( string( "GetChildren not allowed on node " ) + getId() );
+
+ vector< libcmis::ObjectPtr > children;
+@@ -182,7 +185,8 @@ libcmis::DocumentPtr AtomFolder::createDocument( const map< string,
libcmis::Pro
+ AtomLink* childrenLink = getLink( "down", "application/atom+xml;type=feed" );
+
+ if ( ( NULL == childrenLink ) || ( getAllowableActions( ).get() &&
+- !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) ) )
++ !getAllowableActions()->isAllowed( libcmis::ObjectAction::CreateDocument ) &&
++ getAllowableActions()->isDefined( libcmis::ObjectAction::CreateDocument ) ) )
+ throw libcmis::Exception( string( "CreateDocument not allowed on folder " ) + getId() );
+
+ xmlBufferPtr buf = xmlBufferCreate( );
+@@ -210,9 +214,37 @@ libcmis::DocumentPtr AtomFolder::createDocument( const map< string,
libcmis::Pro
+ }
+
+ string respBuf = response->getStream( )->str( );
+- xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL,
0 );
++ xmlDocPtr doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(), NULL,
XML_PARSE_NOERROR );
+ if ( NULL == doc )
+- throw libcmis::Exception( "Failed to parse object infos" );
++ {
++ // We may not have the created document entry in the response body: this is
++ // the behaviour of some servers, but the standard says we need to look for
++ // the Location header.
++ map< string, string >& headers = response->getHeaders( );
++ map< string, string >::iterator it = headers.find( "Location" );
++
++ // Some servers like Lotus Live aren't sending Location header, but Content-Location
++ if ( it == headers.end( ) )
++ it = headers.find( "Content-Location" );
++
++ if ( it != headers.end() )
++ {
++ try
++ {
++ response = getSession( )->httpGetRequest( it->second );
++ respBuf = response->getStream( )->str( );
++ doc = xmlReadMemory( respBuf.c_str(), respBuf.size(), getInfosUrl().c_str(),
NULL, XML_PARSE_NOERROR );
++ }
++ catch ( const CurlException& e )
++ {
++ throw e.getCmisException( );
++ }
++ }
++
++ // if doc is still NULL after that, then throw an exception
++ if ( NULL == doc )
++ throw libcmis::Exception( "Missing expected response from server" );
++ }
+
+ libcmis::ObjectPtr created = getSession( )->createObjectFromEntryDoc( doc );
+ xmlFreeDoc( doc );
+diff --git src/libcmis/atom-object.cxx src/libcmis/atom-object.cxx
+index b7b3b4a..812951d 100644
+--- src/libcmis/atom-object.cxx
++++ src/libcmis/atom-object.cxx
+@@ -140,6 +140,34 @@ libcmis::ObjectPtr AtomObject::updateProperties( const map< string,
libcmis::Pro
+ return updated;
+ }
+
++libcmis::AllowableActionsPtr AtomObject::getAllowableActions( )
++{
++ if ( !m_allowableActions )
++ {
++ // For some reason we had no allowable actions before, get them now.
++ AtomLink* link = getLink(
"http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions",
"application/cmisallowableactions+xml" );
++ if ( link )
++ {
++ try
++ {
++ libcmis::HttpResponsePtr response = getSession()->httpGetRequest( link->getHref()
);
++ string buf = response->getStream()->str();
++ xmlDocPtr doc = xmlReadMemory( buf.c_str(), buf.size(), link->getHref().c_str(),
NULL, 0 );
++ xmlNodePtr actionsNode = xmlDocGetRootElement( doc );
++ if ( actionsNode )
++ m_allowableActions.reset( new libcmis::AllowableActions( actionsNode ) );
++
++ xmlFreeDoc( doc );
++ }
++ catch ( libcmis::Exception& )
++ {
++ }
++ }
++ }
++
++ return libcmis::Object::getAllowableActions();
++}
++
+ void AtomObject::refreshImpl( xmlDocPtr doc ) throw ( libcmis::Exception )
+ {
+ bool createdDoc = ( NULL == doc );
+diff --git src/libcmis/atom-object.hxx src/libcmis/atom-object.hxx
+index 2d1761d..452b4f5 100644
+--- src/libcmis/atom-object.hxx
++++ src/libcmis/atom-object.hxx
+@@ -69,6 +69,8 @@ class AtomObject : public virtual libcmis::Object
+ virtual libcmis::ObjectPtr updateProperties(
+ const std::map< std::string, libcmis::PropertyPtr >& properties ) throw (
libcmis::Exception );
+
++ virtual libcmis::AllowableActionsPtr getAllowableActions( );
++
+ /** Reload the data from the server.
+ */
+ virtual void refresh( ) throw ( libcmis::Exception ) { refreshImpl( NULL ); }
+--
+1.7.10.4
+
diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx
index f1b2b01..c97d014 100644
--- a/sfx2/source/view/sfxbasecontroller.cxx
+++ b/sfx2/source/view/sfxbasecontroller.cxx
@@ -1439,7 +1439,7 @@
{
// CMIS verifications
REFERENCE< document::XCmisDocument > xCmisDoc(
m_pData->m_pViewShell->GetObjectShell()->GetModel(), uno::UNO_QUERY );
- if ( xCmisDoc.is( ) )
+ if ( xCmisDoc.is( ) && xCmisDoc->canCheckOut( ) )
{
beans::PropertyValues aCmisProperties = xCmisDoc->getCmisPropertiesValues( );
diff --git a/ucb/source/ucp/cmis/cmis_content.cxx b/ucb/source/ucp/cmis/cmis_content.cxx
index 5f94d7a..2a78b4e 100644
--- a/ucb/source/ucp/cmis/cmis_content.cxx
+++ b/ucb/source/ucp/cmis/cmis_content.cxx
@@ -307,7 +307,46 @@
if ( NULL == m_pObjectType.get( ) && m_bTransient )
{
string typeId = m_bIsFolder ? "cmis:folder" : "cmis:document";
- m_pObjectType = getSession( xEnv )->getType( typeId );
+ // The type to create needs to be fetched from the possible children types
+ // defined in the parent folder. Then, we'll pick up the first one we find matching
+ // cmis:folder or cmis:document (depending what we need to create).
+ // The easy case will work in most cases, but not on some servers (like Lotus Live)
+ libcmis::Folder* pParent = NULL;
+ bool bTypeRestricted = false;
+ try
+ {
+ pParent = dynamic_cast< libcmis::Folder* >( getObject( xEnv ).get( ) );
+ }
+ catch ( const libcmis::Exception& )
+ {
+ }
+
+ if ( pParent )
+ {
+ map< string, libcmis::PropertyPtr >& aProperties = pParent->getProperties( );
+ map< string, libcmis::PropertyPtr >::iterator it = aProperties.find(
"cmis:allowedChildObjectTypeIds" );
+ if ( it != aProperties.end( ) )
+ {
+ libcmis::PropertyPtr pProperty = it->second;
+ if ( pProperty )
+ {
+ vector< string > typesIds = pProperty->getStrings( );
+ for ( vector< string >::iterator typeIt = typesIds.begin();
+ typeIt != typesIds.end() && !m_pObjectType; ++typeIt )
+ {
+ bTypeRestricted = true;
+ libcmis::ObjectTypePtr type = getSession( xEnv )->getType( *typeIt );
+
+ // FIXME Improve performances by adding getBaseTypeId( ) method to
libcmis
+ if ( type->getBaseType( )->getId( ) == typeId )
+ m_pObjectType = type;
+ }
+ }
+ }
+ }
+
+ if ( !bTypeRestricted )
+ m_pObjectType = getSession( xEnv )->getType( typeId );
}
return m_pObjectType;
}
@@ -318,7 +357,39 @@
if ( !m_pObject.get() )
{
if ( !m_sObjectPath.isEmpty( ) )
- m_pObject = getSession( xEnv )->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath )
);
+ {
+ try
+ {
+ m_pObject = getSession( xEnv )->getObjectByPath( OUSTR_TO_STDSTR(
m_sObjectPath ) );
+ }
+ catch ( const libcmis::Exception& )
+ {
+ // In some cases, getting the object from the path doesn't work,
+ // but getting the parent from its path and the get the child in the list is
OK.
+ // It's weird, but needed to handle case where the path isn't the folders/files
+ // names separated by '/' (as in Lotus Live)
+ INetURLObject aParentUrl( m_sURL );
+ string sName = OUSTR_TO_STDSTR( aParentUrl.getName(
INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET ) );
+ aParentUrl.removeSegment( );
+ rtl::OUString sParentUrl = aParentUrl.GetMainURL( INetURLObject::NO_DECODE );
+
+ Content aParent( m_xContext, m_pProvider, new ucbhelper::ContentIdentifier(
sParentUrl ) );
+ libcmis::FolderPtr pParentFolder = boost::dynamic_pointer_cast<
libcmis::Folder >( aParent.getObject( xEnv ) );
+ if ( pParentFolder )
+ {
+ vector< libcmis::ObjectPtr > children = pParentFolder->getChildren( );
+ for ( vector< libcmis::ObjectPtr >::iterator it = children.begin( );
+ it != children.end() && !m_pObject; ++it )
+ {
+ if ( ( *it )->getName( ) == sName )
+ m_pObject = *it;
+ }
+ }
+
+ if ( !m_pObject )
+ throw libcmis::Exception( "Object not found" );
+ }
+ }
else if (!m_sObjectId.isEmpty( ) )
m_pObject = getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId ) );
else
@@ -643,25 +714,6 @@
return uno::Reference< sdbc::XRow >( xRow.get() );
}
- bool Content::exists( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
- {
- bool bExists = true;
- try
- {
- if ( !m_sObjectPath.isEmpty( ) )
- m_pSession->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath ) );
- else if ( !m_sObjectId.isEmpty( ) )
- getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId ) );
- // No need to handle the root folder case... how can it not exists?
- }
- catch ( const libcmis::Exception& )
- {
- bExists = false;
- }
-
- return bExists;
- }
-
uno::Any Content::open(const ucb::OpenCommandArgument2 & rOpenCommand,
const uno::Reference< ucb::XCommandEnvironment > & xEnv )
throw( uno::Exception )
@@ -669,7 +721,7 @@
bool bIsFolder = isFolder( xEnv );
// Handle the case of the non-existing file
- if ( !exists( xEnv ) )
+ if ( !getObject( xEnv ) )
{
uno::Sequence< uno::Any > aArgs( 1 );
aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier();
@@ -992,7 +1044,17 @@
boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary |
ios_base::in | ios_base::out ) );
uno::Reference < io::XOutputStream > xOutput = new
ucbhelper::StdOutputStream( pOut );
copyData( xInputStream, xOutput );
- document->setContentStream( pOut, OUSTR_TO_STDSTR( rMimeType ), string( ),
bReplaceExisting );
+ try
+ {
+ document->setContentStream( pOut, OUSTR_TO_STDSTR( rMimeType ),
string( ), bReplaceExisting );
+ }
+ catch ( const libcmis::Exception& )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny
+ ( uno::RuntimeException( "Error when setting document content",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ }
}
}
else
@@ -1003,16 +1065,36 @@
if ( bIsFolder )
{
- libcmis::FolderPtr pNew = pFolder->createFolder( m_pObjectProps );
- sNewPath = STD_TO_OUSTR( newPath );
+ try
+ {
+ libcmis::FolderPtr pNew = pFolder->createFolder( m_pObjectProps );
+ sNewPath = STD_TO_OUSTR( newPath );
+ }
+ catch ( const libcmis::Exception& )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny
+ ( uno::RuntimeException( "Error when creating folder",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ }
}
else
{
boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary |
ios_base::in | ios_base::out ) );
uno::Reference < io::XOutputStream > xOutput = new
ucbhelper::StdOutputStream( pOut );
copyData( xInputStream, xOutput );
- pFolder->createDocument( m_pObjectProps, pOut, OUSTR_TO_STDSTR( rMimeType
), string() );
- sNewPath = STD_TO_OUSTR( newPath );
+ try
+ {
+ pFolder->createDocument( m_pObjectProps, pOut, OUSTR_TO_STDSTR(
rMimeType ), string() );
+ sNewPath = STD_TO_OUSTR( newPath );
+ }
+ catch ( const libcmis::Exception& )
+ {
+ ucbhelper::cancelCommandExecution( uno::makeAny
+ ( uno::RuntimeException( "Error when creating document",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ }
}
}
@@ -1336,7 +1418,7 @@
{
URL aCmisUrl( m_sURL );
aUrl.removeSegment( );
- aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::NO_DECODE ) );
+ aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::DECODE_WITH_CHARSET ) );
sRet = aCmisUrl.asString( );
}
}
diff --git a/ucb/source/ucp/cmis/cmis_content.hxx b/ucb/source/ucp/cmis/cmis_content.hxx
index b28c301..7260cb3 100644
--- a/ucb/source/ucp/cmis/cmis_content.hxx
+++ b/ucb/source/ucp/cmis/cmis_content.hxx
@@ -99,8 +99,6 @@
libcmis::Session* getSession( const com::sun::star::uno::Reference<
com::sun::star::ucb::XCommandEnvironment >& xEnv );
libcmis::ObjectTypePtr getObjectType( const com::sun::star::uno::Reference<
com::sun::star::ucb::XCommandEnvironment >& xEnv );
- bool exists( const com::sun::star::uno::Reference< com::sun::star::ucb::XCommandEnvironment >&
xEnv );
-
private:
typedef rtl::Reference< Content > ContentRef;
typedef std::list< ContentRef > ContentRefList;
diff --git a/ucb/source/ucp/cmis/cmis_url.cxx b/ucb/source/ucp/cmis/cmis_url.cxx
index 40d7a25..dc23c9e 100644
--- a/ucb/source/ucp/cmis/cmis_url.cxx
+++ b/ucb/source/ucp/cmis/cmis_url.cxx
@@ -107,13 +107,34 @@
if ( !m_sPath.isEmpty( ) )
{
- if ( m_sPath[0] != '/' )
- sUrl += "/";
- sUrl += m_sPath;
+ sal_Int32 nPos = -1;
+ rtl::OUString sEncodedPath;
+ do
+ {
+ sal_Int32 nStartPos = nPos + 1;
+ nPos = m_sPath.indexOf( '/', nStartPos );
+ sal_Int32 nLen = nPos - nStartPos;
+ if ( nPos == -1 )
+ nLen = m_sPath.getLength( ) - nStartPos;
+ rtl::OUString sSegment = m_sPath.copy( nStartPos, nLen );
+
+ if ( !sSegment.isEmpty( ) )
+ {
+ sEncodedPath += "/" + rtl::Uri::encode( sSegment,
+ rtl_UriCharClassRelSegment,
+ rtl_UriEncodeKeepEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+ }
+ while ( nPos != -1 );
+ sUrl += sEncodedPath;
}
else if ( !m_sId.isEmpty( ) )
{
- sUrl += "#" + m_sId;
+ sUrl += "#" + rtl::Uri::encode( m_sId,
+ rtl_UriCharClassRelSegment,
+ rtl_UriEncodeKeepEscapes,
+ RTL_TEXTENCODING_UTF8 );
}
return sUrl;
--
To view, visit https://gerrit.libreoffice.org/2148
To unsubscribe, visit https://gerrit.libreoffice.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7bb4211db0506998cef40ac1fb6375e647234085
Gerrit-PatchSet: 1
Gerrit-Project: core
Gerrit-Branch: libreoffice-4-0
Gerrit-Owner: Bosdonnat Cedric <cedric.bosdonnat@free.fr>
Context
- [PATCH libreoffice-4-0] CMIS: made it work with Lotus Live · Bosdonnat Cedric (via Code Review)
Privacy Policy |
Impressum (Legal Info) |
Copyright information: Unless otherwise specified, all text and images
on this website are licensed under the
Creative Commons Attribution-Share Alike 3.0 License.
This does not include the source code of LibreOffice, which is
licensed under the Mozilla Public License (
MPLv2).
"LibreOffice" and "The Document Foundation" are
registered trademarks of their corresponding registered owners or are
in actual use as trademarks in one or more countries. Their respective
logos and icons are also subject to international copyright laws. Use
thereof is explained in our
trademark policy.