Kaydet (Commit) d242d9e1 authored tarafından Cédric Bosdonnat's avatar Cédric Bosdonnat

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.
üst 519e576a
......@@ -18,6 +18,7 @@ $(eval $(call gb_UnpackedTarball_add_patches,cmis, \
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 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
......@@ -1439,7 +1439,7 @@ void SfxBaseController::ShowInfoBars( )
{
// 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( );
......
......@@ -307,7 +307,46 @@ namespace cmis
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 @@ namespace cmis
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 @@ namespace cmis
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 @@ namespace cmis
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 @@ namespace cmis
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 @@ namespace cmis
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 @@ namespace cmis
{
URL aCmisUrl( m_sURL );
aUrl.removeSegment( );
aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::NO_DECODE ) );
aCmisUrl.setObjectPath( aUrl.GetURLPath( INetURLObject::DECODE_WITH_CHARSET ) );
sRet = aCmisUrl.asString( );
}
}
......
......@@ -99,8 +99,6 @@ private:
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;
......
......@@ -107,13 +107,34 @@ namespace cmis
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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment