mirror of https://github.com/vitalif/grive2
handling change feed. some good news about trashed documents
parent
a09ba40dbc
commit
935ac608ff
|
@ -76,7 +76,7 @@ Drive::Drive( OAuth2& auth, const Json& options ) :
|
|||
std::atoi(xrsp.Response()["docs:largestChangestamp"]["@value"].front().Value().c_str()) ) ;
|
||||
|
||||
SyncFolders( &http ) ;
|
||||
|
||||
|
||||
Log( "Reading remote server file list", log::info ) ;
|
||||
http.Get( feed_base + "?showfolders=true&showroot=true", &xrsp, m_http_hdr ) ;
|
||||
xml::Node resp = xrsp.Response() ;
|
||||
|
@ -84,84 +84,60 @@ Drive::Drive( OAuth2& auth, const Json& options ) :
|
|||
m_resume_link = resp["link"].
|
||||
Find( "@rel", "http://schemas.google.com/g/2005#resumable-create-media" )["@href"] ;
|
||||
|
||||
// bool has_next = false ;
|
||||
Feed feed( resp ) ;
|
||||
do
|
||||
{
|
||||
// xml::NodeSet entries = resp["entry"] ;
|
||||
for ( Feed::iterator i = feed.begin() ; i != feed.end() ; ++i )
|
||||
{
|
||||
// if ( (*i)["content"] == "" )
|
||||
// continue ;
|
||||
|
||||
Entry entry( *i ) ;
|
||||
if ( entry.Kind() != "folder" )
|
||||
{
|
||||
Resource *parent = m_state.FindByHref( entry.ParentHref() ) ;
|
||||
std::string fn = entry.Filename() ;
|
||||
|
||||
if ( fn.empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( fn.find('/') != fn.npos )
|
||||
Log( "file \"%1%\" contains a slash in its name, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent == 0 || !parent->IsInRootTree() )
|
||||
Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent != 0 && !parent->IsFolder() )
|
||||
Log( "warning: entry %1% has parent %2% which is not a folder, ignored",
|
||||
entry.Title(), parent->Name(), log::verbose ) ;
|
||||
|
||||
else
|
||||
m_state.FromRemote( entry ) ;
|
||||
}
|
||||
}
|
||||
|
||||
// xml::NodeSet nss = resp["link"].Find( "@rel", "next" ) ;
|
||||
// has_next = !nss.empty() ;
|
||||
|
||||
// std::string next_uri = feed.Next() ;
|
||||
// if ( !next_uri.empty() )
|
||||
// {
|
||||
// http.Get( next_uri, &xrsp, m_http_hdr ) ;
|
||||
// resp = xrsp.Response() ;
|
||||
// }
|
||||
std::for_each( feed.begin(), feed.end(), boost::bind( &Drive::FromRemote, this, _1 ) ) ;
|
||||
} while ( feed.GetNext( &http, m_http_hdr ) ) ;
|
||||
|
||||
|
||||
// pull the changes feed
|
||||
boost::format changes_uri( "https://docs.google.com/feeds/default/private/changes?start-index=%1%" ) ;
|
||||
http::ResponseLog log2( "changes-", ".xml", &xrsp ) ;
|
||||
http.Get( (changes_uri%(prev_stamp+1)).str(), &log2, m_http_hdr ) ;
|
||||
|
||||
xml::NodeSet centries = xrsp.Response()["entry"] ;
|
||||
for ( xml::NodeSet::iterator i = centries.begin() ; i != centries.end() ; ++i )
|
||||
{
|
||||
if ( (*i)["content"] == "" )
|
||||
continue ;
|
||||
Feed changes( xrsp.Response() ) ;
|
||||
std::for_each( changes.begin(), changes.end(), boost::bind( &Drive::FromChange, this, _1 ) ) ;
|
||||
}
|
||||
|
||||
Entry entry( *i ) ;
|
||||
if ( entry.Kind() != "folder" )
|
||||
{
|
||||
Resource *parent = m_state.FindByHref( entry.ParentHref() ) ;
|
||||
std::string fn = entry.Filename() ;
|
||||
|
||||
if ( fn.empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( fn.find('/') != fn.npos )
|
||||
Log( "file \"%1%\" contains a slash in its name, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent == 0 || !parent->IsInRootTree() )
|
||||
Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent != 0 && !parent->IsFolder() )
|
||||
Log( "warning: entry %1% has parent %2% which is not a folder, ignored",
|
||||
entry.Title(), parent->Name(), log::verbose ) ;
|
||||
|
||||
else
|
||||
m_state.FromRemote( entry ) ;
|
||||
}
|
||||
void Drive::FromRemote( const Entry& entry )
|
||||
{
|
||||
if ( entry.Kind() != "folder" && !entry.ContentSrc().empty() )
|
||||
{
|
||||
Resource *parent = m_state.FindByHref( entry.ParentHref() ) ;
|
||||
std::string fn = entry.Filename() ;
|
||||
|
||||
if ( fn.empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( fn.find('/') != fn.npos )
|
||||
Log( "file \"%1%\" contains a slash in its name, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent == 0 || !parent->IsInRootTree() )
|
||||
Log( "file \"%1%\" parent doesn't exist, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( parent != 0 && !parent->IsFolder() )
|
||||
Log( "warning: entry %1% has parent %2% which is not a folder, ignored",
|
||||
entry.Title(), parent->Name(), log::verbose ) ;
|
||||
|
||||
else
|
||||
m_state.FromRemote( entry ) ;
|
||||
}
|
||||
}
|
||||
|
||||
void Drive::FromChange( const Entry& entry )
|
||||
{
|
||||
std::string fn = entry.Filename() ;
|
||||
|
||||
if ( entry.IsRemoved() )
|
||||
Log( "file \"%1%\" represents a deletion, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( fn.empty() )
|
||||
Log( "file \"%1%\" is a google document, ignored", entry.Title(), log::verbose ) ;
|
||||
|
||||
else if ( !entry.ContentSrc().empty() )
|
||||
{
|
||||
Log( "changed entry: %1% %2%", entry.Filename(), entry.ContentSrc() ) ;
|
||||
m_state.FromChange( entry ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace http
|
|||
class Agent ;
|
||||
}
|
||||
|
||||
class Entry ;
|
||||
class OAuth2 ;
|
||||
class Json ;
|
||||
|
||||
|
@ -51,6 +52,8 @@ public :
|
|||
private :
|
||||
void SyncFolders( http::Agent *http ) ;
|
||||
void file();
|
||||
void FromRemote( const Entry& entry ) ;
|
||||
void FromChange( const Entry& entry ) ;
|
||||
|
||||
private :
|
||||
OAuth2& m_auth ;
|
||||
|
|
|
@ -38,13 +38,15 @@ Entry::Entry( ) :
|
|||
m_resource_id ( "folder:root" ),
|
||||
m_self_href ( root_href ),
|
||||
m_create_link ( root_create ),
|
||||
m_change_stamp ( -1 )
|
||||
m_change_stamp ( -1 ),
|
||||
m_is_removed ( false )
|
||||
{
|
||||
}
|
||||
|
||||
/// construct an entry for remote
|
||||
Entry::Entry( const xml::Node& n ) :
|
||||
m_change_stamp( -1 )
|
||||
m_change_stamp( -1 ),
|
||||
m_is_removed( false )
|
||||
{
|
||||
Update( n ) ;
|
||||
}
|
||||
|
@ -74,6 +76,7 @@ void Entry::Update( const xml::Node& n )
|
|||
m_filename = n["docs:suggestedFilename"] ;
|
||||
m_content_src = n["content"]["@src"] ;
|
||||
m_self_href = n["link"].Find( "@rel", "self" )["@href"] ;
|
||||
m_alt_self = n["link"].Find( "@rel", "http://schemas.google.com/docs/2007#alt-self" )["@href"] ;
|
||||
m_mtime = DateTime( n["updated"] ) ;
|
||||
|
||||
m_resource_id = n["gd:resourceId"] ;
|
||||
|
@ -92,6 +95,8 @@ void Entry::Update( const xml::Node& n )
|
|||
|
||||
// convert to lower case for easy comparison
|
||||
std::transform( m_md5.begin(), m_md5.end(), m_md5.begin(), tolower ) ;
|
||||
|
||||
m_is_removed = !n["gd:deleted"].empty() || !n["docs:removed"].empty() ;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& Entry::ParentHrefs() const
|
||||
|
@ -147,6 +152,11 @@ std::string Entry::SelfHref() const
|
|||
return m_self_href ;
|
||||
}
|
||||
|
||||
std::string Entry::AltSelf() const
|
||||
{
|
||||
return m_alt_self ;
|
||||
}
|
||||
|
||||
std::string Entry::ParentHref() const
|
||||
{
|
||||
return m_parent_hrefs.empty() ? "" : m_parent_hrefs.front() ;
|
||||
|
@ -189,6 +199,7 @@ void Entry::Swap( Entry& e )
|
|||
m_parent_hrefs.swap( e.m_parent_hrefs ) ;
|
||||
|
||||
m_self_href.swap( e.m_self_href ) ;
|
||||
m_alt_self.swap( e.m_alt_self ) ;
|
||||
m_content_src.swap( e.m_content_src ) ;
|
||||
m_edit_link.swap( e.m_edit_link ) ;
|
||||
m_create_link.swap( e.m_create_link ) ;
|
||||
|
@ -196,6 +207,7 @@ void Entry::Swap( Entry& e )
|
|||
m_mtime.Swap( e.m_mtime ) ;
|
||||
|
||||
std::swap( m_change_stamp, e.m_change_stamp ) ;
|
||||
std::swap( m_is_removed, e.m_is_removed ) ;
|
||||
}
|
||||
|
||||
long Entry::ChangeStamp() const
|
||||
|
@ -203,4 +215,9 @@ long Entry::ChangeStamp() const
|
|||
return m_change_stamp ;
|
||||
}
|
||||
|
||||
bool Entry::IsRemoved() const
|
||||
{
|
||||
return m_is_removed ;
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -59,6 +59,7 @@ public :
|
|||
std::string ETag() const ;
|
||||
|
||||
std::string SelfHref() const ;
|
||||
std::string AltSelf() const ;
|
||||
std::string ParentHref() const ;
|
||||
std::string ContentSrc() const ;
|
||||
std::string EditLink() const ;
|
||||
|
@ -73,6 +74,8 @@ public :
|
|||
|
||||
long ChangeStamp() const ;
|
||||
|
||||
bool IsRemoved() const ;
|
||||
|
||||
private :
|
||||
std::string m_title ;
|
||||
std::string m_filename ;
|
||||
|
@ -84,6 +87,7 @@ private :
|
|||
std::vector<std::string> m_parent_hrefs ;
|
||||
|
||||
std::string m_self_href ;
|
||||
std::string m_alt_self ;
|
||||
std::string m_content_src ;
|
||||
std::string m_edit_link ;
|
||||
std::string m_create_link ;
|
||||
|
@ -91,6 +95,7 @@ private :
|
|||
long m_change_stamp ;
|
||||
|
||||
DateTime m_mtime ;
|
||||
bool m_is_removed ;
|
||||
} ;
|
||||
|
||||
} // end of namespace
|
||||
|
|
|
@ -33,6 +33,12 @@ Feed::Feed( const xml::Node& root ) :
|
|||
{
|
||||
}
|
||||
|
||||
void Feed::Assign( const xml::Node& root )
|
||||
{
|
||||
m_root = root ;
|
||||
m_entries = m_root["entry"] ;
|
||||
}
|
||||
|
||||
Feed::iterator Feed::begin() const
|
||||
{
|
||||
return iterator( m_entries.begin() ) ;
|
||||
|
|
|
@ -43,13 +43,14 @@ public :
|
|||
|
||||
public :
|
||||
explicit Feed( const xml::Node& root ) ;
|
||||
|
||||
void Assign( const xml::Node& root ) ;
|
||||
|
||||
iterator begin() const ;
|
||||
iterator end() const ;
|
||||
|
||||
std::string Next() const ;
|
||||
bool GetNext( http::Agent *http, const http::Header& auth ) ;
|
||||
|
||||
|
||||
private :
|
||||
xml::Node m_root ;
|
||||
xml::NodeSet m_entries ;
|
||||
|
|
|
@ -192,7 +192,7 @@ void Resource::FromRemoteFile( const Entry& remote, const DateTime& last_sync )
|
|||
m_state = local_changed ;
|
||||
}
|
||||
else
|
||||
Trace( "file 1% state is %2%", Name(), m_state ) ;
|
||||
Trace( "file %1% state is %2%", Name(), m_state ) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,19 @@ std::size_t State::TryResolveEntry()
|
|||
return count ;
|
||||
}
|
||||
|
||||
void State::FromChange( const Entry& e )
|
||||
{
|
||||
// entries in the change feed is always treated as remote newer,
|
||||
// so we override the last sync time to 0
|
||||
if ( Resource *res = m_res.FindByHref( e.AltSelf() ) )
|
||||
{
|
||||
Log( "found %1% in change %2%", res->Name(), e.ChangeStamp() ) ;
|
||||
m_res.Update( res, e, DateTime() ) ;
|
||||
}
|
||||
else
|
||||
Log( "can't found %1% %2%", e.Filename(), e.SelfHref() ) ;
|
||||
}
|
||||
|
||||
bool State::Update( const Entry& e )
|
||||
{
|
||||
assert( !e.ParentHref().empty() ) ;
|
||||
|
@ -181,7 +194,6 @@ Resource* State::Find( const fs::path& path )
|
|||
return m_res.FindByPath( path ) ;
|
||||
}
|
||||
|
||||
|
||||
State::iterator State::begin()
|
||||
{
|
||||
return m_res.begin() ;
|
||||
|
|
|
@ -48,6 +48,7 @@ public :
|
|||
|
||||
void FromLocal( const fs::path& p ) ;
|
||||
void FromRemote( const Entry& e ) ;
|
||||
void FromChange( const Entry& e ) ;
|
||||
void ResolveEntry() ;
|
||||
|
||||
void Read( const fs::path& filename ) ;
|
||||
|
|
Loading…
Reference in New Issue