Change backup policy
1 - Reports the error to the user in a popup. 2 - Names of the backups include the timstamp instead of a number. Names have more sense and the files can be sorted by names what is also the age order. 3 - Behaviors when changing configuration improved, the number of files present on the system was not reduced when reducing the number of backups or backups deactivated.
This commit is contained in:
@@ -2263,6 +2263,8 @@ public:
|
||||
BackupPolicy() {
|
||||
policy = Standard;
|
||||
numberOfFiles = 1;
|
||||
useFCBakExtension = false;
|
||||
saveBackupDateFormat = "%Y%m%d-%H%M%S";
|
||||
}
|
||||
~BackupPolicy() {
|
||||
}
|
||||
@@ -2272,6 +2274,12 @@ public:
|
||||
void setNumberOfFiles(int count) {
|
||||
numberOfFiles = count;
|
||||
}
|
||||
void useBackupExtension(bool on) {
|
||||
useFCBakExtension = on;
|
||||
}
|
||||
void setDateFormat(const std::string& fmt) {
|
||||
saveBackupDateFormat = fmt;
|
||||
}
|
||||
void apply(const std::string& sourcename, const std::string& targetname) {
|
||||
switch (policy) {
|
||||
case Standard:
|
||||
@@ -2341,16 +2349,191 @@ private:
|
||||
Base::Console().Warning("Cannot rename file from '%s' to '%s'\n",
|
||||
sourcename.c_str(), targetname.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
void applyTimeStamp(const std::string& sourcename, const std::string& targetname) {
|
||||
(void)sourcename;
|
||||
(void)targetname;
|
||||
Base::FileInfo fi(targetname);
|
||||
|
||||
std::string fn = sourcename;
|
||||
std::string ext = fi.extension();
|
||||
std::string bn; // full path with no extension but with "."
|
||||
std::string pbn; // base name of the project + "."
|
||||
if (!ext.empty()) {
|
||||
bn = fi.filePath().substr(0, fi.filePath().length() - ext.length());
|
||||
pbn = fi.fileName().substr(0, fi.fileName().length() - ext.length());
|
||||
}
|
||||
else {
|
||||
bn = fi.filePath() + ".";
|
||||
pbn = fi.fileName() + ".";
|
||||
}
|
||||
|
||||
bool backupManagementError = false; // Note error and report at the end
|
||||
if (fi.exists()) {
|
||||
// replace . by - in format to avoid . between base name and extension
|
||||
boost::replace_all(saveBackupDateFormat, ".", "-");
|
||||
{
|
||||
// Remove all extra backups
|
||||
std::string fn = fi.fileName();
|
||||
Base::FileInfo di(fi.dirPath());
|
||||
std::vector<Base::FileInfo> backup;
|
||||
std::vector<Base::FileInfo> files = di.getDirectoryContent();
|
||||
for (std::vector<Base::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
|
||||
if (it->isFile()) {
|
||||
std::string file = it->fileName();
|
||||
std::string fext = it->extension();
|
||||
std::string fextUp = fext;
|
||||
std::transform(fextUp.begin(), fextUp.end(), fextUp.begin(),(int (*)(int))toupper);
|
||||
// re-enforcing identification of the backup file
|
||||
|
||||
|
||||
// old case : the name starts with the full name of the project and follows with numbers
|
||||
if ((startsWith(file, fn) &&
|
||||
(file.length() > fn.length()) &&
|
||||
checkDigits(file.substr(fn.length()))) ||
|
||||
// .FCBak case : The bame starts with the base name of the project + "."
|
||||
// + complement with no "." + ".FCBak"
|
||||
((fextUp == "FCBAK") && startsWith(file, pbn) &&
|
||||
(checkValidComplement(file, pbn, fext)))) {
|
||||
backup.push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup.empty() && (int)backup.size() >= numberOfFiles) {
|
||||
std::sort (backup.begin(), backup.end(), fileComparisonByDate);
|
||||
// delete the oldest backup file we found
|
||||
// Base::FileInfo del = backup.front();
|
||||
int nb = 0;
|
||||
for (std::vector<Base::FileInfo>::iterator it = backup.begin(); it != backup.end(); ++it) {
|
||||
nb++;
|
||||
if (nb >= numberOfFiles) {
|
||||
try {
|
||||
if (!it->deleteFile()) {
|
||||
backupManagementError = true;
|
||||
Base::Console().Warning("Cannot remove backup file : %s\n", it->fileName().c_str());
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
backupManagementError = true;
|
||||
Base::Console().Warning("Cannot remove backup file : %s\n", it->fileName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} //end remove backup
|
||||
|
||||
// create a new backup file
|
||||
{
|
||||
int ext = 1;
|
||||
if (useFCBakExtension) {
|
||||
std::stringstream str;
|
||||
Base::TimeInfo ti = fi.lastModified();
|
||||
time_t s =ti.getSeconds();
|
||||
struct tm * timeinfo = localtime(& s);
|
||||
char buffer[100];
|
||||
|
||||
strftime(buffer,sizeof(buffer),saveBackupDateFormat.c_str(),timeinfo);
|
||||
str << bn << buffer ;
|
||||
|
||||
fn = str.str();
|
||||
bool done = false;
|
||||
|
||||
if ((fn == "") || (fn[fn.length()-1] == ' ') || (fn[fn.length()-1] == '-')) {
|
||||
if (fn[fn.length()-1] == ' ') {
|
||||
fn = fn.substr(0,fn.length()-1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (renameFileNoErase(fi, fn+".FCBak") == false) {
|
||||
fn = fn + "-";
|
||||
}
|
||||
else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
while (ext < numberOfFiles + 10) {
|
||||
if (renameFileNoErase(fi, fn+std::to_string(ext)+".FCBak"))
|
||||
break;
|
||||
ext++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// changed but simpler and solves also the delay sometimes introduced by google drive
|
||||
while (ext < numberOfFiles + 10) {
|
||||
// linux just replace the file if exists, and then the existence is to be tested before rename
|
||||
if (renameFileNoErase(fi, fi.filePath()+std::to_string(ext)))
|
||||
break;
|
||||
ext++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext >= numberOfFiles + 10) {
|
||||
Base::Console().Error("File not saved: Cannot rename project file to backup file\n");
|
||||
//throw Base::FileException("File not saved: Cannot rename project file to backup file", fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base::FileInfo tmp(sourcename);
|
||||
if (tmp.renameFile(targetname.c_str()) == false) {
|
||||
Base::Console().Error("Save interrupted: Cannot rename file from '%s' to '%s'\n",
|
||||
sourcename.c_str(), targetname.c_str());
|
||||
//throw Base::FileException("Save interrupted: Cannot rename temporary file to project file", tmp);
|
||||
}
|
||||
|
||||
if (numberOfFiles <= 0) {
|
||||
try {
|
||||
fi.deleteFile();
|
||||
}
|
||||
catch (...) {
|
||||
Base::Console().Warning("Cannot remove backup file: %s\n", fi.fileName().c_str());
|
||||
backupManagementError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (backupManagementError) {
|
||||
throw Base::FileException("Warning: Save complete, but error while managing backup history.", fi);
|
||||
}
|
||||
}
|
||||
static bool fileComparisonByDate(const Base::FileInfo& i,
|
||||
const Base::FileInfo& j) {
|
||||
return (i.lastModified()>j.lastModified());
|
||||
}
|
||||
bool startsWith(const std::string& st1,
|
||||
const std::string& st2) const {
|
||||
return st1.substr(0,st2.length()) == st2;
|
||||
}
|
||||
bool checkValidString (const std::string& cmpl, const boost::regex& e) const {
|
||||
boost::smatch what;
|
||||
bool res = boost::regex_search (cmpl,what,e);
|
||||
return res;
|
||||
}
|
||||
bool checkValidComplement(const std::string& file, const std::string& pbn, const std::string& ext) const {
|
||||
std::string cmpl = file.substr(pbn.length(),file.length()- pbn.length() - ext.length()-1);
|
||||
boost::regex e (R"(^[^.]*$)");
|
||||
return checkValidString(cmpl,e);
|
||||
}
|
||||
bool checkDigits (const std::string& cmpl) const {
|
||||
boost::regex e (R"(^[0-9]*$)");
|
||||
return checkValidString(cmpl,e);
|
||||
}
|
||||
bool renameFileNoErase(Base::FileInfo fi, const std::string& newName) {
|
||||
// linux just replaces the file if it exists, so the existence is to be tested before rename
|
||||
Base::FileInfo nf(newName);
|
||||
if (!nf.exists()) {
|
||||
return fi.renameFile(newName.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Policy policy;
|
||||
int numberOfFiles;
|
||||
bool useFCBakExtension;
|
||||
std::string saveBackupDateFormat;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2419,9 +2602,20 @@ bool Document::saveToFile(const char* filename) const
|
||||
if (!backup) {
|
||||
count_bak = -1;
|
||||
}
|
||||
bool useFCBakExtension = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/Document")->GetBool("UseFCBakExtension",false);
|
||||
std::string saveBackupDateFormat = App::GetApplication().GetParameterGroupByPath
|
||||
("User parameter:BaseApp/Preferences/Document")->GetASCII("SaveBackupDateFormat","%Y%m%d-%H%M%S");
|
||||
|
||||
BackupPolicy policy;
|
||||
policy.setPolicy(BackupPolicy::Standard);
|
||||
if (useFCBakExtension) {
|
||||
policy.setPolicy(BackupPolicy::TimeStamp);
|
||||
policy.useBackupExtension(useFCBakExtension);
|
||||
policy.setDateFormat(saveBackupDateFormat);
|
||||
}
|
||||
else {
|
||||
policy.setPolicy(BackupPolicy::Standard);
|
||||
}
|
||||
policy.setNumberOfFiles(count_bak);
|
||||
policy.apply(fn, filename);
|
||||
}
|
||||
|
||||
@@ -482,6 +482,9 @@ bool FileInfo::renameFile(const char* NewName)
|
||||
int code = errno;
|
||||
std::clog << "Error in renameFile: " << strerror(code) << " (" << code << ")" << std::endl;
|
||||
}
|
||||
else {
|
||||
FileName = NewName;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>720</height>
|
||||
<height>820</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -33,7 +33,16 @@
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@@ -44,7 +53,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -78,7 +96,16 @@
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -334,7 +361,16 @@ automatically run a file recovery when it is started.</string>
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
@@ -387,6 +423,58 @@ automatically run a file recovery when it is started.</string>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="prefSaveBackupExtension">
|
||||
<property name="text">
|
||||
<string>Use date and FCBak extension</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>UseFCBakExtension</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Document</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Date format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefLineEdit" name="prefSaveBackupDateFormat">
|
||||
<property name="text">
|
||||
<string notr="true">%Y%m%d-%H%M%S</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>SaveBackupDateFormat</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Document</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="Gui::PrefCheckBox" name="prefAddLogo">
|
||||
<property name="toolTip">
|
||||
@@ -667,12 +755,12 @@ You can also use the form: John Doe <john@doe.com></string>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>106</x>
|
||||
<y>325</y>
|
||||
<x>128</x>
|
||||
<y>486</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>479</x>
|
||||
<y>326</y>
|
||||
<x>584</x>
|
||||
<y>488</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
@@ -683,12 +771,44 @@ You can also use the form: John Doe <john@doe.com></string>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>196</x>
|
||||
<y>253</y>
|
||||
<x>218</x>
|
||||
<y>385</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>275</x>
|
||||
<y>254</y>
|
||||
<x>582</x>
|
||||
<y>387</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>prefSaveBackupFiles</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>prefSaveBackupExtension</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>258</x>
|
||||
<y>474</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>182</x>
|
||||
<y>519</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>prefSaveBackupFiles</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>prefSaveBackupDateFormat</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>304</x>
|
||||
<y>477</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>380</x>
|
||||
<y>499</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
||||
@@ -46,6 +46,12 @@ DlgSettingsDocumentImp::DlgSettingsDocumentImp( QWidget* parent )
|
||||
ui->prefSaveTransaction->hide();
|
||||
ui->prefDiscardTransaction->hide();
|
||||
|
||||
QString tip = QString::fromLatin1("<html><head/><body><p>%1</p>"
|
||||
"<p>%2: %Y%m%d-%H%M%S</p>"
|
||||
"<p>%3: <a href=\"http://www.cplusplus.com/reference/ctime/strftime/\">C++ strftime</a>"
|
||||
"</p></body></html>").arg(tr("The format of the date to use."), tr("Default"), tr("Format"));
|
||||
ui->prefSaveBackupDateFormat->setToolTip(tip);
|
||||
|
||||
ui->prefCountBackupFiles->setMaximum(INT_MAX);
|
||||
ui->prefCompression->setMinimum(Z_NO_COMPRESSION);
|
||||
ui->prefCompression->setMaximum(Z_BEST_COMPRESSION);
|
||||
@@ -74,6 +80,8 @@ void DlgSettingsDocumentImp::saveSettings()
|
||||
ui->prefAddLogo->onSave();
|
||||
ui->prefSaveBackupFiles->onSave();
|
||||
ui->prefCountBackupFiles->onSave();
|
||||
ui->prefSaveBackupExtension->onSave();
|
||||
ui->prefSaveBackupDateFormat->onSave();
|
||||
ui->prefDuplicateLabel->onSave();
|
||||
ui->prefPartialLoading->onSave();
|
||||
ui->prefLicenseType->onSave();
|
||||
@@ -105,6 +113,8 @@ void DlgSettingsDocumentImp::loadSettings()
|
||||
ui->prefAddLogo->onRestore();
|
||||
ui->prefSaveBackupFiles->onRestore();
|
||||
ui->prefCountBackupFiles->onRestore();
|
||||
ui->prefSaveBackupExtension->onRestore();
|
||||
ui->prefSaveBackupDateFormat->onRestore();
|
||||
ui->prefDuplicateLabel->onRestore();
|
||||
ui->prefPartialLoading->onRestore();
|
||||
ui->prefLicenseType->onRestore();
|
||||
|
||||
Reference in New Issue
Block a user