core: support for migration of annots and forms out of docdata/

This commit is contained in:
Fabio D'Urso 2014-09-09 16:08:32 +02:00
parent 3e1b1ff04c
commit e059d2652c
6 changed files with 132 additions and 18 deletions

View file

@ -606,22 +606,22 @@ qulonglong DocumentPrivate::getFreeMemory( qulonglong *freeSwap )
#endif
}
void DocumentPrivate::loadDocumentInfo( LoadDocumentInfoFlags loadWhat )
bool DocumentPrivate::loadDocumentInfo( LoadDocumentInfoFlags loadWhat )
// note: load data and stores it internally (document or pages). observers
// are still uninitialized at this point so don't access them
{
//kDebug(OkularDebug).nospace() << "Using '" << d->m_xmlFileName << "' as document info file.";
if ( m_xmlFileName.isEmpty() )
return;
return false;
QFile infoFile( m_xmlFileName );
loadDocumentInfo( infoFile, loadWhat );
return loadDocumentInfo( infoFile, loadWhat );
}
void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat )
bool DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat )
{
if ( !infoFile.exists() || !infoFile.open( QIODevice::ReadOnly ) )
return;
return false;
// Load DOM from XML file
QDomDocument doc( "documentInfo" );
@ -629,15 +629,16 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
{
kDebug(OkularDebug) << "Can't load XML pair! Check for broken xml.";
infoFile.close();
return;
return false;
}
infoFile.close();
QDomElement root = doc.documentElement();
if ( root.tagName() != "documentInfo" )
return;
return false;
KUrl documentUrl( root.attribute( "url" ) );
bool loadedAnything = false; // set if something gets actually loaded
// Parse the DOM tree
QDomNode topLevelNode = root.firstChild();
@ -645,7 +646,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
{
QString catName = topLevelNode.toElement().tagName();
// Restore page attributes (bookmark, annotations, ...) from the DOM
// Restore page attributes (form data, annotations, ...) from the DOM
if ( catName == "pageList" && ( loadWhat & LoadPageInfo ) )
{
QDomNode pageNode = topLevelNode.firstChild();
@ -660,7 +661,10 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
// pass the domElement to the right page, to read config data from
if ( ok && pageNumber >= 0 && pageNumber < (int)m_pagesVector.count() )
m_pagesVector[ pageNumber ]->d->restoreLocalContents( pageElement );
{
if ( m_pagesVector[ pageNumber ]->d->restoreLocalContents( pageElement ) )
loadedAnything = true;
}
}
pageNode = pageNode.nextSibling();
}
@ -689,6 +693,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
QString vpString = historyElement.attribute( "viewport" );
m_viewportIterator = m_viewportHistory.insert( m_viewportHistory.end(),
DocumentViewport( vpString ) );
loadedAnything = true;
}
historyNode = historyNode.nextSibling();
}
@ -704,6 +709,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
if ( ok && newrotation != 0 )
{
setRotationInternal( newrotation, false );
loadedAnything = true;
}
}
else if ( infoElement.tagName() == "views" )
@ -720,6 +726,7 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
if ( view->name() == viewName )
{
loadViewsInfo( view, viewElement );
loadedAnything = true;
break;
}
}
@ -733,6 +740,8 @@ void DocumentPrivate::loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags l
topLevelNode = topLevelNode.nextSibling();
} // </documentInfo>
return loadedAnything;
}
void DocumentPrivate::loadViewsInfo( View *view, const QDomElement &e )
@ -1198,8 +1207,8 @@ void DocumentPrivate::saveDocumentInfo() const
doc.appendChild( root );
// 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM
// -> skipped for archives, because they store such info in their internal metadata.xml
if ( !m_archiveData )
// -> do this there are not-yet-migrated annots or forms in docdata/
if ( m_docdataMigrationNeeded )
{
QDomElement pageList = doc.createElement( "pageList" );
root.appendChild( pageList );
@ -2219,6 +2228,7 @@ Document::OpenResult Document::openDocument( const QString & docFile, const KUrl
p->d->m_doc = d;
d->m_metadataLoadingCompleted = false;
d->m_docdataMigrationNeeded = false;
// 2. load Additional Data (bookmarks, local annotations and metadata) about the document
if ( d->m_archiveData )
@ -2228,7 +2238,9 @@ Document::OpenResult Document::openDocument( const QString & docFile, const KUrl
}
else
{
d->loadDocumentInfo( LoadAllInfo );
if ( d->loadDocumentInfo( LoadPageInfo ) )
d->m_docdataMigrationNeeded = true;
d->loadDocumentInfo( LoadGeneralInfo );
}
d->m_metadataLoadingCompleted = true;
@ -2460,6 +2472,7 @@ void Document::closeDocument()
AudioPlayer::instance()->d->m_currentDocument = KUrl();
d->m_undoStack->clear();
d->m_docdataMigrationNeeded = false;
}
void Document::addObserver( DocumentObserver * pObserver )
@ -2707,7 +2720,9 @@ KUrl Document::currentDocument() const
bool Document::isAllowed( Permission action ) const
{
if ( action == Okular::AllowNotes && !d->m_annotationEditingEnabled )
if ( action == Okular::AllowNotes && ( d->m_docdataMigrationNeeded || !d->m_annotationEditingEnabled ) )
return false;
if ( action == Okular::AllowFillForms && d->m_docdataMigrationNeeded )
return false;
#if !OKULAR_FORCE_DRM
@ -4432,6 +4447,20 @@ void Document::walletDataForFile( const QString &fileName, QString *walletName,
}
}
bool Document::isDocdataMigrationNeeded() const
{
return d->m_docdataMigrationNeeded;
}
void Document::docdataMigrationDone()
{
if (d->m_docdataMigrationNeeded)
{
d->m_docdataMigrationNeeded = false;
foreachObserver( notifySetup( d->m_pagesVector, 0 ) );
}
}
void DocumentPrivate::requestDone( PixmapRequest * req )
{
if ( !req )

View file

@ -886,6 +886,25 @@ class OKULAR_EXPORT Document : public QObject
*/
void walletDataForFile( const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey ) const;
/**
* Since version 0.21, okular does not allow editing annotations and
* form data if they are stored in the docdata directory (like older
* okular versions did by default).
* If this flag is set, then annotations and forms cannot be edited.
*
* @since 0.21
*/
bool isDocdataMigrationNeeded() const;
/**
* Delete annotations and form data from the docdata folder. Call it if
* isDocdataMigrationNeeded() was true and you've just saved them to an
* external file.
*
* @since 0.21
*/
void docdataMigrationDone();
public Q_SLOTS:
/**
* This slot is called whenever the user changes the @p rotation of

View file

@ -106,7 +106,8 @@ class DocumentPrivate
m_archiveData( 0 ),
m_fontsCached( false ),
m_annotationEditingEnabled ( true ),
m_annotationBeingMoved( false )
m_annotationBeingMoved( false ),
m_docdataMigrationNeeded( false )
{
calculateMaxTextPages();
}
@ -123,8 +124,8 @@ class DocumentPrivate
void calculateMaxTextPages();
qulonglong getTotalMemory();
qulonglong getFreeMemory( qulonglong *freeSwap = 0 );
void loadDocumentInfo( LoadDocumentInfoFlags loadWhat );
void loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat );
bool loadDocumentInfo( LoadDocumentInfoFlags loadWhat );
bool loadDocumentInfo( QFile &infoFile, LoadDocumentInfoFlags loadWhat );
void loadViewsInfo( View *view, const QDomElement &e );
void saveViewsInfo( View *view, QDomElement &e ) const;
QString giveAbsolutePath( const QString & fileName ) const;
@ -280,6 +281,13 @@ class DocumentPrivate
QUndoStack *m_undoStack;
QDomNode m_prevPropsOfAnnotBeingModified;
// Since 0.21, we no longer support saving annotations and form data in
// the docdata/ directory and we ask the user to migrate them to an
// external file as soon as possible, otherwise the document will be
// shown in read-only mode. This flag is set if the docdata/ XML file
// for the current document contains any annotation or form.
bool m_docdataMigrationNeeded;
};
class DocumentInfoPrivate

View file

@ -785,8 +785,10 @@ void Page::deleteAnnotations()
m_annotations.clear();
}
void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
bool PagePrivate::restoreLocalContents( const QDomNode & pageNode )
{
bool loadedAnything = false; // set if something actually gets loaded
// iterate over all chilren (annotationList, ...)
QDomNode childNode = pageNode.firstChild();
while ( childNode.isElement() )
@ -821,6 +823,7 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
{
m_doc->performAddPageAnnotation(m_number, annotation);
kDebug(OkularDebug) << "restored annot:" << annotation->uniqueName();
loadedAnything = true;
}
else
kWarning(OkularDebug).nospace() << "page (" << m_number << "): can't restore an annotation from XML.";
@ -868,9 +871,12 @@ void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
QString value = formElement.attribute( "value" );
(*wantedIt)->d_ptr->setValue( value );
loadedAnything = true;
}
}
}
return loadedAnything;
}
void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & document, PageItems what ) const

View file

@ -68,7 +68,7 @@ class PagePrivate
/**
* Loads the local contents (e.g. annotations) of the page.
*/
void restoreLocalContents( const QDomNode & pageNode );
bool restoreLocalContents( const QDomNode & pageNode );
/**
* Saves the local contents (e.g. annotations) of the page.

View file

@ -11,9 +11,12 @@
#include <threadweaver/ThreadWeaver.h>
#include "../core/annotations.h"
#include "../core/document.h"
#include "../core/document_p.h"
#include "../core/generator.h"
#include "../core/observer.h"
#include "../core/page.h"
#include "../core/rotationjob_p.h"
#include "../settings_core.h"
@ -24,6 +27,7 @@ class DocumentTest
private slots:
void testCloseDuringRotationJob();
void testDocdataMigration();
};
// Test that we don't crash if the document is closed while a RotationJob
@ -58,5 +62,53 @@ void DocumentTest::testCloseDuringRotationJob()
qApp->processEvents();
}
// Test that, if there's a XML file in docdata referring to a document, we
// detect that it must be migrated, that it doesn't get wiped out if you close
// the document without migrating and that it does get wiped out after migrating
void DocumentTest::testDocdataMigration()
{
Okular::SettingsCore::instance( "documenttest" );
const KUrl testFileUrl("file://" KDESRCDIR "data/file1.pdf");
const QString testFilePath = testFileUrl.toLocalFile();
const qint64 testFileSize = QFileInfo(testFilePath).size();
// Copy XML file to the docdata/ directory
const QString docDataPath = Okular::DocumentPrivate::docDataFileName(testFileUrl, testFileSize);
QFile::remove(docDataPath);
QVERIFY( QFile::copy(KDESRCDIR "data/file1-docdata.xml", docDataPath) );
// Open our document
Okular::Document *m_document = new Okular::Document( 0 );
const KMimeType::Ptr mime = KMimeType::findByPath( testFilePath );
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
// Check that the annotation from file1-docdata.xml was loaded
QCOMPARE( m_document->page( 0 )->annotations().size(), 1 );
QCOMPARE( m_document->page( 0 )->annotations().first()->uniqueName(), QString("testannot") );
// Check that we detect that it must be migrated
QCOMPARE( m_document->isDocdataMigrationNeeded(), true );
m_document->closeDocument();
// Reopen the document and check that the annotation is still present
// (because we have not migrated)
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
QCOMPARE( m_document->page( 0 )->annotations().size(), 1 );
QCOMPARE( m_document->page( 0 )->annotations().first()->uniqueName(), QString("testannot") );
QCOMPARE( m_document->isDocdataMigrationNeeded(), true );
// Pretend the user has done the migration
m_document->docdataMigrationDone();
QCOMPARE( m_document->isDocdataMigrationNeeded(), false );
m_document->closeDocument();
// Now the docdata file should have no annotations, let's check
QCOMPARE( m_document->openDocument( testFilePath, testFileUrl, mime ), Okular::Document::OpenSuccess );
QCOMPARE( m_document->page( 0 )->annotations().size(), 0 );
QCOMPARE( m_document->isDocdataMigrationNeeded(), false );
m_document->closeDocument();
}
QTEST_KDEMAIN( DocumentTest, GUI )
#include "documenttest.moc"