новые иконки в OpenBoard
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
OpenBoard/src/core/UBForeignObjectsHandler.cpp

646 lines
19 KiB

/*
* Copyright (C) 2015-2018 Département de l'Instruction Publique (DIP-SEM)
*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of OpenBoard.
*
* OpenBoard 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, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* OpenBoard 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 OpenBoard. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UBForeignObjectsHandler.h"
#include <QtGui>
#include <QtXml>
#include "UBSettings.h"
const QString tVideo = "video";
const QString tAudio = "audio";
const QString tImage = "image";
const QString tForeignObject = "foreignObject";
const QString tTeacherGuide = "teacherGuide";
const QString tMedia = "media";
const QString tGroups = "groups";
const QString aHref = "xlink:href";
const QString aType = "ub:type";
const QString aReqExt = "requiredExtensions";
const QString aSrc = "ub:src";
const QString aMediaType = "mediaType";
const QString aRelativePath = "relativePath";
const QString aActionMedia = "ub:actionFirstParameter";
const QString vText = "text";
const QString vReqExt = "http://ns.adobe.com/pdf/1.3/";
const QString wgtSuff = ".wgt";
const QString thumbSuff = ".png";
const QString scanDirs = "audios,images,videos,teacherGuideObjects,widgets";
const QStringList trashFilter = QStringList() << "*.swf";
static QString strIdFrom(const QString &filePath)
{
if ((filePath).isEmpty()) {
return QString();
}
QRegExp rx("\\{.(?!.*\\{).*\\}");
if (rx.indexIn(filePath) == -1) {
return QString();
}
return rx.cap();
}
static bool rm_r(const QString &rmPath)
{
QFileInfo fi(rmPath);
if (!fi.exists()) {
qDebug() << rmPath << "does not exist";
return false;
} else if (fi.isFile()) {
if (!QFile::remove(rmPath)) {
qDebug() << "can't remove file" << rmPath;
return false;
}
return true;
} else if (fi.isDir()) {
QFileInfoList fList = QDir(rmPath).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
foreach (QFileInfo sub, fList) {
rm_r(sub.absoluteFilePath());
}
if (!QDir().rmdir(rmPath)) {
qDebug() << "can't remove dir" << rmPath;
return false;
}
return true;
}
return false;
}
static bool cp_rf(const QString &what, const QString &where)
{
QFileInfo whatFi(what);
QFileInfo whereFi = QFileInfo(where);
if (!whatFi.exists()) {
qDebug() << what << "does not exist" << Q_FUNC_INFO;
return false;
} else if (whatFi.isFile()) {
QString whereDir = where.section("/", 0, -2, QString::SectionSkipEmpty | QString::SectionIncludeLeadingSep);
QString newFilePath = where;
if (!whereFi.exists()) {
QDir().mkpath(whereDir);
} else if (whereFi.isDir()) {
newFilePath = whereDir + "/" + whatFi.fileName();
}
if (QFile::exists(newFilePath)) {
QFile::remove(newFilePath);
}
if (!QFile::copy(what, newFilePath)) {
qDebug() << "can't copy" << what << "to" << where << Q_FUNC_INFO;
return false;
}
return true;
} else if (whatFi.isDir()) {
if (whereFi.isFile() && whereFi.fileName().toLower() == whatFi.fileName().toLower()) {
qDebug() << "can't copy dir" << what << "to file" << where << Q_FUNC_INFO;
return false;
} else if (whereFi.isDir()) {
rm_r(where);
}
QDir().mkpath(where);
QFileInfoList fList = QDir(what).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
foreach (QFileInfo sub, fList) {
if (!cp_rf(sub.absoluteFilePath(), where + "/" + sub.fileName()))
return false;
}
return true;
}
return true;
}
static QString thumbFileNameFrom(const QString &filePath)
{
if (filePath.isEmpty()) {
return QString();
}
QString thumbPath = filePath;
thumbPath.replace(QRegExp("[\\{\\}]"), "").replace(wgtSuff, thumbSuff);
return thumbPath;
}
QString svgPageName(int pageIndex)
{
return QString("page%1.svg").arg(pageIndex, 3, 10, QLatin1Char('0'));
}
static QDomDocument createDomFromSvg(const QString &svgUrl)
{
Q_ASSERT(QFile::exists(svgUrl));
QString mFoldersXmlStorageName = svgUrl;
if (QFileInfo(mFoldersXmlStorageName).exists()) {
QDomDocument xmlDom;
QFile inFile(mFoldersXmlStorageName);
if (inFile.open(QIODevice::ReadOnly)) {
QString domString(inFile.readAll());
int errorLine = 0; int errorColumn = 0;
QString errorStr;
if (xmlDom.setContent(domString, &errorStr, &errorLine, &errorColumn)) {
return xmlDom;
} else {
qDebug() << "Error reading content of " << mFoldersXmlStorageName << endl
<< "Error:" << inFile.errorString()
<< "Line:" << errorLine
<< "Column:" << errorColumn;
}
inFile.close();
} else {
qDebug() << "Error reading" << mFoldersXmlStorageName << endl
<< "Error:" << inFile.errorString();
}
}
return QDomDocument();
}
class Cleaner
{
public:
void cure(const QUrl &dir)
{
mCurrentDir = dir.toLocalFile();
cleanTrash();
// Gathering information from svg files
QFileInfoList svgInfos = QDir(mCurrentDir).entryInfoList(QStringList() << "*.svg", QDir::NoDotAndDotDot | QDir::Files);
foreach (QFileInfo svgInfo, svgInfos) {
cureIdsFromSvgDom(createDomFromSvg(svgInfo.absoluteFilePath()));
}
fitIdsFromFileSystem();
QVector<QString> deleteCandidates;
findRedundandElements(deleteCandidates);
foreach (QString key, deleteCandidates) {
QString delPath = mPresentIdsMap.value(key);
if (delPath.isNull()) {
continue;
} else if (delPath.endsWith(wgtSuff)) { //remove corresponding thumb
QString thumbPath = thumbFileNameFrom(delPath);
//N/C - NNE - 20140417
if (QFile::exists(thumbPath)) {
rm_r(thumbPath);
}
}
rm_r(delPath);
// Clear parent dir if empty
QDir dir(delPath);
dir.cdUp();
if (dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) {
dir.rmdir(dir.absolutePath());
}
}
qDebug() << "Ok on cure";
}
private:
void cleanTrash()
{
QFileInfoList ifs = QDir(mCurrentDir).entryInfoList(trashFilter, QDir::NoDotAndDotDot | QDir::Files);
foreach (QFileInfo ifo, ifs) {
rm_r(ifo.absoluteFilePath());
}
}
void cureIdsFromSvgDom(const QDomDocument &dom)
{
Q_ASSERT(!dom.isNull());
QDomElement nextElement = dom.documentElement().firstChildElement();
while (!nextElement.isNull()) {
QString nextTag = nextElement.tagName();
qDebug() << "Tag name of the next parsed element is" << nextTag;
if (nextTag == tGroups)
{
nextElement = nextElement.firstChildElement("group");
}
invokeFromText(nextTag, nextElement);
nextElement = nextElement.nextSiblingElement();
}
}
void fitIdsFromFileSystem()
{
QString absPrefix = mCurrentDir + "/";
QStringList dirsList = scanDirs.split(",", QString::SkipEmptyParts);
foreach (QString dirName, dirsList) {
QString absPath = absPrefix + dirName;
if (!QFile::exists(absPath)) {
continue;
}
fitIdsFromDir(absPath);
}
}
void fitIdsFromDir(const QString &scanDir)
{
QFileInfoList fileList = QDir(scanDir).entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
foreach (QFileInfo nInfo, fileList) {
QString uid = strIdFrom(nInfo.fileName());
if (uid.isNull()) {
continue;
}
mPresentIdsMap.insert(uid, nInfo.absoluteFilePath());
}
}
void findRedundandElements(QVector<QString> &v)
{
// Taking information from the physical file system
QStringList domIds = mDomIdsMap.keys();
QStringList presentIds = mPresentIdsMap.keys();
v.resize(qMax(domIds.count(), presentIds.count()));
QVector<QString>::iterator it_diff;
it_diff=std::set_symmetric_difference(domIds.begin(), domIds.end()
, presentIds.begin(), presentIds.end()
, v.begin());
v.resize(it_diff - v.begin());
}
void invokeFromText(const QString &what, const QDomElement &element)
{
if (what == tVideo
|| what == tAudio
|| what == tImage) {
mediaToContainer(element);
} else if (what == tForeignObject) {
foreingObjectToContainer(element);
//N/C - NNE - 20140317
cleanObjectFolder(element);
//N/C - NNE - 20140520
//foreign object may referer resource which are not present in the svg
addResourceIdToSvg(element);
} else if (what == tTeacherGuide) {
teacherGuideToContainer(element);
}
pullActionFromElement(element);
}
// N/C - NNE - 20140317 : When export, reduce the size of the ubz file
void cleanObjectFolder(const QDomElement &element)
{
//QDomElement preference = element.firstChildElement("ub:preference");
//N/C - NNE - 20141021
QDomNodeList childrenNode = element.elementsByTagName("ub:preference");
QVector<QString> objectsIdUsed;
for(int i = 0; i < childrenNode.size(); i++){
QDomElement preference = childrenNode.at(i).toElement();
if(!preference.isNull()){
QString value = preference.attribute("value");
int findPos = value.indexOf("objects/");
int endPos;
//find all objects used
while(findPos != -1){
endPos = value.indexOf("\"", findPos);
objectsIdUsed << value.mid(findPos, endPos - findPos);
findPos = value.indexOf("objects/", endPos);
}
}
}
//N/C - NNE - 20141021 : END
QString path = element.attribute(aSrc);
QString objectsFolderPath = mCurrentDir + "/" + path + "/objects/";
QDir dir(objectsFolderPath);
dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
//then check all files in the objects directory
//delete the file not used (not in te objectIdUsed variable)
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); i++) {
QFileInfo fileInfo = list.at(i);
if(!objectsIdUsed.contains("objects/"+fileInfo.fileName())){
QFile(fileInfo.filePath()).remove();
}
}
}
// N/C - NNE - 20140317 : END
//N/C - NNE - 20140520
void addResourceIdToSvg(const QDomElement& element)
{
QDomElement textContent = element.firstChildElement("itemTextContent");
QString value = textContent.text();
int findPos = value.indexOf("images/");
int endPos;
//find all objects used
while(findPos != -1){
endPos = value.indexOf("\"", findPos);
QString path = value.mid(findPos, endPos - findPos);
QString uuid = path.split("/").at(1).split(".").at(0);
mDomIdsMap.insert(uuid, path);
findPos = value.indexOf("images/", endPos);
}
}
//N/C - NNE - 20140520 : END
void pullActionFromElement(const QDomElement &element)
{
if (!element.hasAttribute(aActionMedia)) {
return;
}
QString path = element.attribute(aActionMedia);
if (path.isNull()) {
return;
}
QString uid = strIdFrom(path);
if (uid.isNull()) {
return;
}
mDomIdsMap.insert(uid, path);
}
void teacherGuideToContainer(const QDomElement &element)
{
QDomElement nMediaElement = element.firstChildElement(tMedia);
while (!nMediaElement.isNull()) {
QString path = nMediaElement.attribute(aRelativePath);
if (path.isNull()) {
continue;
}
QString uid = strIdFrom(path);
if (uid.isNull()) {
return;
}
mDomIdsMap.insert(uid, path);
nMediaElement = nMediaElement.nextSiblingElement(tMedia);
}
}
void mediaToContainer(const QDomElement &element)
{
QString path = element.attribute(aHref);
if (path.isNull()) {
return;
}
QString uid = strIdFrom(path);
if (uid.isNull()) {
return;
}
mDomIdsMap.insert(uid, path);
}
void foreingObjectToContainer(const QDomElement &element)
{
QString type = element.attribute(aType);
if (type == vText) { // We don't have to care of the text object
return;
}
QString path = element.attribute(aSrc);
if (path.isNull()) {
return;
}
QString uid = strIdFrom(path);
if (uid.isNull()) {
return;
}
mDomIdsMap.insert(uid, path);
}
private:
QString mCurrentDir;
QDomDocument mSvgData;
QMap<QString, QString> mDomIdsMap;
QMap<QString, QString> mPresentIdsMap;
};
class PageCopier
{
public:
void copyPage (const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex)
{
mFromDir = fromDir.toLocalFile();
mToDir = toDir.toLocalFile();
mFromIndex = fromIndex;
mToIndex = toIndex;
QString svgFrom = mFromDir + "/" + svgPageName(fromIndex);
QString svgTo = toDir.toLocalFile() + "/" + svgPageName(toIndex);
QDomDocument dd = createDomFromSvg(svgFrom);
QFile fl(svgTo);
if (!fl.open(QIODevice::WriteOnly)) {
qDebug() << Q_FUNC_INFO << "can't open" << fl.fileName() << "for writing";
return;
}
cureIdsFromSvgDom(dd);
QTextStream str(&fl);
dd.save(str, 0);
fl.close();
qDebug() << Q_FUNC_INFO;
}
private:
void cureIdsFromSvgDom(const QDomDocument &dom)
{
Q_ASSERT(!dom.isNull());
QDomElement nextElement = dom.documentElement().firstChildElement();
while (!nextElement.isNull( )) {
qDebug() << "Tag name of the next parsed element is" << nextElement.tagName();
QString nextTag = nextElement.tagName();
cureFromText(nextTag, nextElement);
nextElement = nextElement.nextSiblingElement();
}
}
void cureFromText(const QString &tagName, QDomElement element)
{
if (tagName == tVideo
|| tagName == tAudio
|| tagName == tImage) {
QString newRelative = cureNCopy(element.attribute(aHref));
element.setAttribute(aHref, newRelative);
if (element.hasAttribute(aActionMedia)) {
QString newActionPath = cureNCopy(element.attribute(aActionMedia));
element.setAttribute(aActionMedia, newActionPath);
}
} else if (tagName == tForeignObject) {
//Pdf object is a special case. Detect if it ends with #reference
QString reqExt = element.attribute(aReqExt);
if (reqExt == vReqExt) { //pdf reference
QString ref = element.attribute(aHref);
int i = ref.indexOf("#page");
QString dest = ref.replace(i, ref.length()-i, "");
if (!QFileInfo::exists(dest))
cureNCopy(dest, false);
if (ref.isEmpty()) {
return;
}
ref.replace(QRegExp("^(.*pdf\\#page\\=).*$"), QString("\\1%1").arg(mToIndex));
return;
}
QString type = element.attribute(aType);
if (type == vText) { // We don't have to care of the text object
if (element.hasAttribute(aActionMedia)) {
QString newRelative = cureNCopy(element.attribute(aActionMedia));
element.setAttribute(aActionMedia, newRelative);
}
return;
}
QString newRelative = cureNCopy(element.attribute(aSrc));
element.setAttribute(aSrc, newRelative);
} else if (tagName == tTeacherGuide) {
QDomElement nMediaElement = element.firstChildElement(tMedia);
while (!nMediaElement.isNull()) {
QString newRelative = cureNCopy(nMediaElement.attribute(aRelativePath));
nMediaElement.setAttribute(aRelativePath, newRelative);
nMediaElement = nMediaElement.nextSiblingElement(tMedia);
}
}
}
QString cureNCopy(const QString &relativePath, bool createNewUuid=true)
{
QString relative = relativePath;
if (createNewUuid)
{
QUuid newUuid = QUuid::createUuid();
QString newPath = relative.replace(QRegExp("\\{.*\\}"), newUuid.toString());
cp_rf(mFromDir + "/" + relativePath, mToDir + "/" + newPath);
return newPath;
}
else
{
cp_rf(mFromDir + "/" + relativePath, mToDir + "/" + relativePath);
return relativePath;
}
}
private:
QString mFromDir;
QString mToDir;
int mFromIndex;
int mToIndex;
};
class UBForeighnObjectsHandlerPrivate {
UBForeighnObjectsHandlerPrivate(UBForeighnObjectsHandler *pq)
: q(pq)
{
}
public:
void cure(const QUrl &dir)
{
Cleaner *cleaner = new Cleaner;
cleaner->cure(dir);
delete cleaner;
cleaner = 0;
}
void copyPage (const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex)
{
PageCopier *copier = new PageCopier;
copier->copyPage(fromDir, fromIndex, toDir, toIndex);
delete copier;
copier = 0;
}
private:
UBForeighnObjectsHandler *q;
friend class UBForeighnObjectsHandler;
};
UBForeighnObjectsHandler::UBForeighnObjectsHandler()
: d(new UBForeighnObjectsHandlerPrivate(this))
{
}
UBForeighnObjectsHandler::~UBForeighnObjectsHandler()
{
delete d;
}
void UBForeighnObjectsHandler::cure(const QList<QUrl> &dirs)
{
foreach (QUrl dir, dirs) {
cure(dir);
}
}
void UBForeighnObjectsHandler::cure(const QUrl &dir)
{
d->cure(dir);
}
void UBForeighnObjectsHandler::copyPage(const QUrl &fromDir, int fromIndex, const QUrl &toDir, int toIndex)
{
d->copyPage(fromDir, fromIndex, toDir, toIndex);
}