2018-03-28 11:36:36 +02:00
#include "PresetUpdater.hpp"
2018-04-12 20:04:48 +02:00
#include <algorithm>
2018-03-28 11:36:36 +02:00
#include <thread>
2018-04-24 18:06:42 +02:00
#include <unordered_map>
2018-04-23 11:16:47 +02:00
#include <ostream>
2018-04-13 15:08:58 +02:00
#include <stdexcept>
#include <boost/format.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/algorithm/string.hpp>
2018-04-11 13:12:08 +02:00
#include <boost/filesystem.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/filesystem/fstream.hpp>
2018-12-20 17:44:24 +01:00
#include <boost/lexical_cast.hpp>
2018-04-17 11:54:59 +02:00
#include <boost/log/trivial.hpp>
2018-03-28 11:36:36 +02:00
#include <wx/app.h>
#include <wx/event.h>
2018-04-12 20:04:48 +02:00
#include <wx/msgdlg.h>
2018-03-28 11:36:36 +02:00
2018-04-17 16:59:53 +02:00
#include "libslic3r/libslic3r.h"
2018-03-28 11:36:36 +02:00
#include "libslic3r/Utils.hpp"
#include "slic3r/GUI/GUI.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/I18N.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/GUI/PresetBundle.hpp"
2018-04-24 18:06:42 +02:00
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/GUI_App.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/Utils/Http.hpp"
2018-04-12 20:04:48 +02:00
#include "slic3r/Config/Version.hpp"
#include "slic3r/Config/Snapshot.hpp"
2018-03-28 11:36:36 +02:00
namespace fs = boost :: filesystem ;
2018-04-12 20:04:48 +02:00
using Slic3r :: GUI :: Config :: Index ;
using Slic3r :: GUI :: Config :: Version ;
using Slic3r :: GUI :: Config :: Snapshot ;
using Slic3r :: GUI :: Config :: SnapshotDB ;
2018-03-28 11:36:36 +02:00
namespace Slic3r {
enum {
SLIC3R_VERSION_BODY_MAX = 256 ,
};
2018-04-13 15:08:58 +02:00
static const char * INDEX_FILENAME = "index.idx" ;
static const char * TMP_EXTENSION = ".download" ;
2018-04-12 20:04:48 +02:00
struct Update
{
fs :: path source ;
fs :: path target ;
Version version ;
2018-04-24 18:06:42 +02:00
Update ( fs :: path && source , fs :: path && target , const Version & version ) :
source ( std :: move ( source )),
2018-04-12 20:04:48 +02:00
target ( std :: move ( target )),
version ( version )
{}
2018-04-24 18:06:42 +02:00
std :: string name () const { return source . stem (). string (); }
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
friend std :: ostream & operator << ( std :: ostream & os , const Update & self ) {
os << "Update(" << self . source . string () << " -> " << self . target . string () << ')' ;
2018-04-23 11:16:47 +02:00
return os ;
}
2018-04-12 20:04:48 +02:00
};
2018-04-24 18:06:42 +02:00
struct Incompat
{
fs :: path bundle ;
Version version ;
Incompat ( fs :: path && bundle , const Version & version ) :
bundle ( std :: move ( bundle )),
version ( version )
{}
std :: string name () const { return bundle . stem (). string (); }
friend std :: ostream & operator << ( std :: ostream & os , const Incompat & self ) {
os << "Incompat(" << self . bundle . string () << ')' ;
return os ;
}
};
struct Updates
{
std :: vector < Incompat > incompats ;
std :: vector < Update > updates ;
};
2018-04-12 20:04:48 +02:00
2018-03-28 11:36:36 +02:00
struct PresetUpdater :: priv
{
2018-04-13 15:08:58 +02:00
std :: vector < Index > index_db ;
bool enabled_version_check ;
bool enabled_config_update ;
std :: string version_check_url ;
2018-04-17 17:11:56 +02:00
bool had_config_update ;
2018-03-28 11:36:36 +02:00
fs :: path cache_path ;
2018-04-12 20:04:48 +02:00
fs :: path rsrc_path ;
fs :: path vendor_path ;
2018-03-28 11:36:36 +02:00
bool cancel ;
std :: thread thread ;
2018-09-21 01:33:41 +02:00
priv ();
2018-03-28 11:36:36 +02:00
2018-04-13 15:08:58 +02:00
void set_download_prefs ( AppConfig * app_config );
bool get_file ( const std :: string & url , const fs :: path & target_path ) const ;
void prune_tmps () const ;
void sync_version () const ;
2018-12-20 17:44:24 +01:00
void sync_config ( const std :: set < VendorProfile > vendors );
2018-04-12 20:04:48 +02:00
void check_install_indices () const ;
2018-04-23 13:58:03 +02:00
Updates get_config_updates () const ;
2018-04-18 11:40:43 +02:00
void perform_updates ( Updates && updates , bool snapshot = true ) const ;
2018-06-11 17:34:06 +02:00
static void copy_file ( const fs :: path & from , const fs :: path & to );
2018-03-28 11:36:36 +02:00
};
2018-09-21 01:33:41 +02:00
PresetUpdater :: priv :: priv () :
2018-04-17 17:11:56 +02:00
had_config_update ( false ),
2018-03-28 11:36:36 +02:00
cache_path ( fs :: path ( Slic3r :: data_dir ()) / "cache" ),
2018-04-12 20:04:48 +02:00
rsrc_path ( fs :: path ( resources_dir ()) / "profiles" ),
vendor_path ( fs :: path ( Slic3r :: data_dir ()) / "vendor" ),
2018-03-28 11:36:36 +02:00
cancel ( false )
{
2018-09-21 01:33:41 +02:00
set_download_prefs ( GUI :: wxGetApp (). app_config );
2018-04-13 15:08:58 +02:00
check_install_indices ();
2018-12-20 17:44:24 +01:00
index_db = Index :: load_db ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Pull relevant preferences from AppConfig
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: set_download_prefs ( AppConfig * app_config )
{
enabled_version_check = app_config -> get ( "version_check" ) == "1" ;
2018-04-25 17:43:01 +02:00
version_check_url = app_config -> version_check_url ();
2018-05-15 12:14:26 +02:00
enabled_config_update = app_config -> get ( "preset_update" ) == "1" && ! app_config -> legacy_datadir ();
2018-04-13 15:08:58 +02:00
}
2018-04-23 11:16:47 +02:00
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
2018-04-13 15:08:58 +02:00
bool PresetUpdater :: priv :: get_file ( const std :: string & url , const fs :: path & target_path ) const
{
bool res = false ;
fs :: path tmp_path = target_path ;
2018-04-20 11:05:00 +02:00
tmp_path += ( boost :: format ( ".%1%%2%" ) % get_current_pid () % TMP_EXTENSION ). str ();
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Get: `%1%` \n\t -> `%2%` \n\t via tmp path `%3%`" )
% url
% target_path . string ()
% tmp_path . string ();
2018-04-13 15:08:58 +02:00
Http :: get ( url )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
if ( cancel ) { cancel = true ; }
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error getting: `%1%`: HTTP %2%, %3%" )
% url
% http_status
2018-04-24 18:06:42 +02:00
% error ;
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-04-13 15:08:58 +02:00
fs :: fstream file ( tmp_path , std :: ios :: out | std :: ios :: binary | std :: ios :: trunc );
file . write ( body . c_str (), body . size ());
2018-04-16 16:52:11 +02:00
file . close ();
2018-04-13 15:08:58 +02:00
fs :: rename ( tmp_path , target_path );
res = true ;
})
. perform_sync ();
return res ;
}
2018-04-23 11:16:47 +02:00
// Remove leftover paritally downloaded files, if any.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: prune_tmps () const
{
for ( fs :: directory_iterator it ( cache_path ); it != fs :: directory_iterator (); ++ it ) {
if ( it -> path (). extension () == TMP_EXTENSION ) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "Cache prune: " << it -> path (). string ();
2018-04-13 15:08:58 +02:00
fs :: remove ( it -> path ());
}
}
}
2018-04-23 11:16:47 +02:00
// Get Slic3rPE version available online, save in AppConfig.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: sync_version () const
{
if ( ! enabled_version_check ) { return ; }
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Downloading Slic3rPE online version from: `%1%`" ) % version_check_url ;
2018-04-13 15:08:58 +02:00
Http :: get ( version_check_url )
2018-03-28 11:36:36 +02:00
. size_limit ( SLIC3R_VERSION_BODY_MAX )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
cancel = this -> cancel ;
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Error getting: `%1%`: HTTP %2%, %3%" )
% version_check_url
% http_status
2018-04-24 18:06:42 +02:00
% error ;
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-03-28 11:36:36 +02:00
boost :: trim ( body );
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Got Slic3rPE online version: `%1%`. Sending to GUI thread..." ) % body ;
2018-09-21 01:33:41 +02:00
// wxCommandEvent* evt = new wxCommandEvent(version_online_event);
// evt->SetString(body);
// GUI::get_app()->QueueEvent(evt);
GUI :: wxGetApp (). app_config -> set ( "version_online" , body );
GUI :: wxGetApp (). app_config -> save ();
2018-03-28 11:36:36 +02:00
})
. perform_sync ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
2018-12-20 17:44:24 +01:00
void PresetUpdater :: priv :: sync_config ( const std :: set < VendorProfile > vendors )
2018-04-13 15:08:58 +02:00
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Syncing configuration cache" ;
2018-04-13 15:08:58 +02:00
if ( ! enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Donwload vendor preset bundles
2018-12-20 17:44:24 +01:00
for ( auto & index : index_db ) {
2018-03-28 11:36:36 +02:00
if ( cancel ) { return ; }
2018-04-13 15:08:58 +02:00
const auto vendor_it = vendors . find ( VendorProfile ( index . vendor ()));
2018-04-23 11:16:47 +02:00
if ( vendor_it == vendors . end ()) {
BOOST_LOG_TRIVIAL ( warning ) << "No such vendor: " << index . vendor ();
continue ;
}
2018-03-28 11:36:36 +02:00
2018-04-13 15:08:58 +02:00
const VendorProfile & vendor = * vendor_it ;
2018-04-23 11:16:47 +02:00
if ( vendor . config_update_url . empty ()) {
BOOST_LOG_TRIVIAL ( info ) << "Vendor has no config_update_url: " << vendor . name ;
continue ;
}
2018-04-13 15:08:58 +02:00
// Download a fresh index
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading index for vendor: " << vendor . name ;
2018-04-13 15:08:58 +02:00
const auto idx_url = vendor . config_update_url + "/" + INDEX_FILENAME ;
2018-12-20 17:44:24 +01:00
const std :: string idx_path = ( cache_path / ( vendor . id + ".idx" )). string ();
const std :: string idx_path_temp = idx_path + "-update" ;
if ( ! get_file ( idx_url , idx_path_temp )) { continue ; }
2018-04-13 15:08:58 +02:00
if ( cancel ) { return ; }
// Load the fresh index up
2018-12-20 17:44:24 +01:00
{
Index new_index ;
try {
new_index . load ( idx_path_temp );
} catch ( const std :: exception & err ) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Failed loading a downloaded index %1% for vendor %2%: invalid index?" ) % idx_path_temp % vendor . name ;
continue ;
}
if ( new_index . version () < index . version ()) {
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index." ) % idx_path_temp % vendor . name ;
continue ;
}
Slic3r :: rename_file ( idx_path_temp , idx_path );
index = std :: move ( new_index );
if ( cancel )
return ;
}
2018-04-13 15:08:58 +02:00
// See if a there's a new version to download
2018-12-20 17:44:24 +01:00
const auto recommended_it = index . recommended ();
if ( recommended_it == index . end ()) {
2018-05-01 10:35:07 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "No recommended version for vendor: %1%, invalid index?" ) % vendor . name ;
2018-04-23 11:16:47 +02:00
continue ;
}
2018-04-13 15:08:58 +02:00
const auto recommended = recommended_it -> config_version ;
2018-06-19 18:26:38 +02:00
BOOST_LOG_TRIVIAL ( debug ) << boost :: format ( "Got index for vendor: %1%: current version: %2%, recommended version: %3%" )
2018-04-23 11:16:47 +02:00
% vendor . name
% vendor . config_version . to_string ()
% recommended . to_string ();
2018-04-13 15:08:58 +02:00
if ( vendor . config_version >= recommended ) { continue ; }
// Download a fresh bundle
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading new bundle for vendor: " << vendor . name ;
2018-04-13 15:08:58 +02:00
const auto bundle_url = ( boost :: format ( "%1%/%2%.ini" ) % vendor . config_update_url % recommended . to_string ()). str ();
const auto bundle_path = cache_path / ( vendor . id + ".ini" );
if ( ! get_file ( bundle_url , bundle_path )) { continue ; }
if ( cancel ) { return ; }
2018-03-28 11:36:36 +02:00
}
}
2018-04-23 11:16:47 +02:00
// Install indicies from resources. Only installs those that are either missing or older than in resources.
2018-04-12 20:04:48 +02:00
void PresetUpdater :: priv :: check_install_indices () const
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking if indices need to be installed from resources..." ;
2018-04-12 20:04:48 +02:00
for ( fs :: directory_iterator it ( rsrc_path ); it != fs :: directory_iterator (); ++ it ) {
const auto & path = it -> path ();
if ( path . extension () == ".idx" ) {
const auto path_in_cache = cache_path / path . filename ();
if ( ! fs :: exists ( path_in_cache )) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Install index from resources: " << path . filename ();
2018-06-11 17:34:06 +02:00
copy_file ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
} else {
Index idx_rsrc , idx_cache ;
idx_rsrc . load ( path );
idx_cache . load ( path_in_cache );
if ( idx_cache . version () < idx_rsrc . version ()) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Update index from resources: " << path . filename ();
2018-06-11 17:34:06 +02:00
copy_file ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
}
2018-04-12 20:04:48 +02:00
}
}
}
}
2018-04-23 11:16:47 +02:00
// Generates a list of bundle updates that are to be performed
2018-04-23 13:58:03 +02:00
Updates PresetUpdater :: priv :: get_config_updates () const
2018-04-12 20:04:48 +02:00
{
Updates updates ;
2018-04-23 13:58:03 +02:00
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking for cached configuration updates..." ;
2018-04-12 20:04:48 +02:00
for ( const auto idx : index_db ) {
2018-04-24 18:06:42 +02:00
auto bundle_path = vendor_path / ( idx . vendor () + ".ini" );
2018-04-12 20:04:48 +02:00
if ( ! fs :: exists ( bundle_path )) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Bundle not present for index, skipping: " << idx . vendor ();
2018-04-12 20:04:48 +02:00
continue ;
}
// Perform a basic load and check the version
const auto vp = VendorProfile :: from_ini ( bundle_path , false );
const auto ver_current = idx . find ( vp . config_version );
if ( ver_current == idx . end ()) {
2018-07-24 15:27:31 +02:00
auto message = ( boost :: format ( "Preset bundle `%1%` version not found in index: %2%" ) % idx . vendor () % vp . config_version . to_string ()). str ();
BOOST_LOG_TRIVIAL ( error ) << message ;
throw std :: runtime_error ( message );
2018-04-12 20:04:48 +02:00
}
2018-07-21 09:32:45 +02:00
// Getting a recommended version from the latest index, wich may have been downloaded
// from the internet, or installed / updated from the installation resources.
2018-04-12 20:04:48 +02:00
const auto recommended = idx . recommended ();
if ( recommended == idx . end ()) {
2018-05-01 10:35:07 +02:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "No recommended version for vendor: %1%, invalid index?" ) % idx . vendor ();
2018-04-12 20:04:48 +02:00
}
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( debug ) << boost :: format ( "Vendor: %1%, version installed: %2%, version cached: %3%" )
% vp . name
2018-04-24 18:06:42 +02:00
% ver_current -> config_version . to_string ()
% recommended -> config_version . to_string ();
2018-04-23 11:16:47 +02:00
2018-04-12 20:04:48 +02:00
if ( ! ver_current -> is_current_slic3r_supported ()) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( warning ) << "Current Slic3r incompatible with installed bundle: " << bundle_path . string ();
2018-04-24 18:06:42 +02:00
updates . incompats . emplace_back ( std :: move ( bundle_path ), * ver_current );
2018-04-12 20:04:48 +02:00
} else if ( recommended -> config_version > ver_current -> config_version ) {
// Config bundle update situation
2018-04-23 13:58:03 +02:00
// Check if the update is already present in a snapshot
const auto recommended_snap = SnapshotDB :: singleton (). snapshot_with_vendor_preset ( vp . name , recommended -> config_version );
if ( recommended_snap != SnapshotDB :: singleton (). end ()) {
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Bundle update %1% %2% already found in snapshot %3%, skipping..." )
% vp . name
% recommended -> config_version . to_string ()
% recommended_snap -> id ;
continue ;
}
2018-06-19 18:26:38 +02:00
auto path_src = cache_path / ( idx . vendor () + ".ini" );
2018-07-21 09:32:45 +02:00
auto path_in_rsrc = rsrc_path / ( idx . vendor () + ".ini" );
2018-06-19 18:26:38 +02:00
if ( ! fs :: exists ( path_src )) {
if ( ! fs :: exists ( path_in_rsrc )) {
BOOST_LOG_TRIVIAL ( warning ) << boost :: format ( "Index for vendor %1% indicates update, but bundle found in neither cache nor resources" )
2018-07-21 09:32:45 +02:00
% idx . vendor ();
2018-06-19 18:26:38 +02:00
continue ;
} else {
path_src = std :: move ( path_in_rsrc );
2018-07-21 09:32:45 +02:00
path_in_rsrc . clear ();
2018-06-19 18:26:38 +02:00
}
2018-04-13 15:08:58 +02:00
}
2018-07-21 09:32:45 +02:00
auto new_vp = VendorProfile :: from_ini ( path_src , false );
bool found = false ;
2018-06-19 18:26:38 +02:00
if ( new_vp . config_version == recommended -> config_version ) {
updates . updates . emplace_back ( std :: move ( path_src ), std :: move ( bundle_path ), * recommended );
2018-07-21 09:32:45 +02:00
found = true ;
} else if ( ! path_in_rsrc . empty () && fs :: exists ( path_in_rsrc )) {
new_vp = VendorProfile :: from_ini ( path_in_rsrc , false );
if ( new_vp . config_version == recommended -> config_version ) {
updates . updates . emplace_back ( std :: move ( path_in_rsrc ), std :: move ( bundle_path ), * recommended );
found = true ;
}
}
if ( ! found )
2018-06-19 18:26:38 +02:00
BOOST_LOG_TRIVIAL ( warning ) << boost :: format ( "Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources" )
2018-05-02 17:13:39 +02:00
% idx . vendor ()
2018-06-19 18:26:38 +02:00
% recommended -> config_version . to_string ();
2018-04-12 20:04:48 +02:00
}
}
return updates ;
}
2018-04-18 11:40:43 +02:00
void PresetUpdater :: priv :: perform_updates ( Updates && updates , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_DOWNGRADE );
2018-04-24 18:06:42 +02:00
}
2018-04-23 11:16:47 +02:00
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% incompatible bundles" ) % updates . incompats . size ();
for ( const auto & incompat : updates . incompats ) {
BOOST_LOG_TRIVIAL ( info ) << '\t' << incompat ;
fs :: remove ( incompat . bundle );
}
2018-04-18 11:40:43 +02:00
}
2018-04-24 18:06:42 +02:00
else if ( updates . updates . size () > 0 ) {
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_UPGRADE );
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Performing %1% updates" ) % updates . updates . size ();
2018-04-23 11:16:47 +02:00
2018-04-24 18:06:42 +02:00
for ( const auto & update : updates . updates ) {
BOOST_LOG_TRIVIAL ( info ) << '\t' << update ;
2018-04-16 16:52:11 +02:00
2018-06-11 17:34:06 +02:00
copy_file ( update . source , update . target );
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
PresetBundle bundle ;
bundle . load_configbundle ( update . target . string (), PresetBundle :: LOAD_CFGBNDLE_SYSTEM );
2018-04-16 16:52:11 +02:00
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% conflicting presets" )
% ( bundle . prints . size () + bundle . filaments . size () + bundle . printers . size ());
2018-04-24 18:06:42 +02:00
auto preset_remover = []( const Preset & preset ) {
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << preset . file ;
2018-04-24 18:06:42 +02:00
fs :: remove ( preset . file );
};
for ( const auto & preset : bundle . prints ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . filaments ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . printers ) { preset_remover ( preset ); }
2018-05-16 19:41:35 +02:00
// Also apply the `obsolete_presets` property, removing obsolete ini files
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Deleting %1% obsolete presets" )
% ( bundle . obsolete_presets . prints . size () + bundle . obsolete_presets . filaments . size () + bundle . obsolete_presets . printers . size ());
2018-05-16 19:41:35 +02:00
auto obsolete_remover = []( const char * subdir , const std :: string & preset ) {
auto path = fs :: path ( Slic3r :: data_dir ()) / subdir / preset ;
path += ".ini" ;
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << path . string ();
2018-05-16 19:41:35 +02:00
fs :: remove ( path );
};
for ( const auto & name : bundle . obsolete_presets . prints ) { obsolete_remover ( "print" , name ); }
for ( const auto & name : bundle . obsolete_presets . filaments ) { obsolete_remover ( "filament" , name ); }
2018-11-19 11:10:22 +01:00
for ( const auto & name : bundle . obsolete_presets . sla_prints ) { obsolete_remover ( "sla_print" , name ); }
for ( const auto & name : bundle . obsolete_presets . sla_materials /*filaments*/ ) { obsolete_remover ( "sla_material" , name ); }
2018-05-16 19:41:35 +02:00
for ( const auto & name : bundle . obsolete_presets . printers ) { obsolete_remover ( "printer" , name ); }
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
}
}
2018-06-11 17:34:06 +02:00
void PresetUpdater :: priv :: copy_file ( const fs :: path & source , const fs :: path & target )
{
static const auto perms = fs :: owner_read | fs :: owner_write | fs :: group_read | fs :: others_read ; // aka 644
// Make sure the file has correct permission both before and after we copy over it
2018-06-11 18:30:40 +02:00
if ( fs :: exists ( target )) {
fs :: permissions ( target , perms );
}
2018-06-11 17:34:06 +02:00
fs :: copy_file ( source , target , fs :: copy_option :: overwrite_if_exists );
fs :: permissions ( target , perms );
}
2018-04-12 20:04:48 +02:00
2018-09-21 01:33:41 +02:00
PresetUpdater :: PresetUpdater () :
p ( new priv ())
2018-04-13 15:08:58 +02:00
{}
2018-03-28 11:36:36 +02:00
// Public
PresetUpdater ::~ PresetUpdater ()
{
if ( p && p -> thread . joinable ()) {
2018-04-23 11:16:47 +02:00
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
2018-03-28 11:36:36 +02:00
p -> cancel = true ;
p -> thread . join ();
}
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: sync ( PresetBundle * preset_bundle )
2018-03-28 11:36:36 +02:00
{
2018-09-21 01:33:41 +02:00
p -> set_download_prefs ( GUI :: wxGetApp (). app_config );
2018-04-13 15:08:58 +02:00
if ( ! p -> enabled_version_check && ! p -> enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
std :: set < VendorProfile > vendors = preset_bundle -> vendors ;
p -> thread = std :: move ( std :: thread ([ this , vendors ]() {
2018-04-13 15:08:58 +02:00
this -> p -> prune_tmps ();
this -> p -> sync_version ();
this -> p -> sync_config ( std :: move ( vendors ));
2018-03-28 11:36:36 +02:00
}));
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: slic3r_update_notify ()
{
2018-04-23 11:16:47 +02:00
if ( ! p -> enabled_version_check ) { return ; }
if ( p -> had_config_update ) {
BOOST_LOG_TRIVIAL ( info ) << "New Slic3r version available, but there was a configuration update, notification won't be displayed" ;
return ;
}
2018-04-17 16:59:53 +02:00
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2018-04-17 16:59:53 +02:00
const auto ver_slic3r = Semver :: parse ( SLIC3R_VERSION );
2018-04-20 14:53:11 +02:00
const auto ver_online_str = app_config -> get ( "version_online" );
const auto ver_online = Semver :: parse ( ver_online_str );
const auto ver_online_seen = Semver :: parse ( app_config -> get ( "version_online_seen" ));
2018-04-17 16:59:53 +02:00
if ( ! ver_slic3r ) {
throw std :: runtime_error ( "Could not parse Slic3r version string: " SLIC3R_VERSION );
}
2018-04-20 14:53:11 +02:00
if ( ver_online ) {
// Only display the notification if the version available online is newer AND if we haven't seen it before
if ( * ver_online > * ver_slic3r && ( ! ver_online_seen || * ver_online_seen < * ver_online )) {
2018-04-24 18:06:42 +02:00
GUI :: MsgUpdateSlic3r notification ( * ver_slic3r , * ver_online );
2018-04-20 14:53:11 +02:00
notification . ShowModal ();
if ( notification . disable_version_check ()) {
app_config -> set ( "version_check" , "0" );
p -> enabled_version_check = false ;
}
2018-04-17 16:59:53 +02:00
}
2018-04-20 14:53:11 +02:00
app_config -> set ( "version_online_seen" , ver_online_str );
2018-04-17 16:59:53 +02:00
}
}
2018-04-24 18:06:42 +02:00
bool PresetUpdater :: config_update () const
2018-04-11 13:12:08 +02:00
{
2018-04-24 18:06:42 +02:00
if ( ! p -> enabled_config_update ) { return true ; }
2018-04-13 15:08:58 +02:00
2018-04-23 13:58:03 +02:00
auto updates = p -> get_config_updates ();
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "%1% bundles incompatible. Asking for action..." ) % updates . incompats . size ();
2018-04-23 11:16:47 +02:00
2018-04-24 18:40:06 +02:00
std :: unordered_map < std :: string , wxString > incompats_map ;
2018-04-24 18:06:42 +02:00
for ( const auto & incompat : updates . incompats ) {
auto vendor = incompat . name ();
2018-07-23 12:34:07 +02:00
const auto min_slic3r = incompat . version . min_slic3r_version ;
const auto max_slic3r = incompat . version . max_slic3r_version ;
wxString restrictions ;
if ( min_slic3r != Semver :: zero () && max_slic3r != Semver :: inf ()) {
restrictions = wxString :: Format ( _ ( L ( "requires min. %s and max. %s" )),
min_slic3r . to_string (),
max_slic3r . to_string ()
);
} else if ( min_slic3r != Semver :: zero ()) {
restrictions = wxString :: Format ( _ ( L ( "requires min. %s" )), min_slic3r . to_string ());
} else {
restrictions = wxString :: Format ( _ ( L ( "requires max. %s" )), max_slic3r . to_string ());
}
2018-04-24 18:40:06 +02:00
incompats_map . emplace ( std :: make_pair ( std :: move ( vendor ), std :: move ( restrictions )));
2018-04-12 20:04:48 +02:00
}
2018-06-22 15:27:04 +02:00
p -> had_config_update = true ; // This needs to be done before a dialog is shown because of OnIdle() + CallAfter() in Perl
2018-04-24 18:06:42 +02:00
GUI :: MsgDataIncompatible dlg ( std :: move ( incompats_map ));
const auto res = dlg . ShowModal ();
if ( res == wxID_REPLACE ) {
BOOST_LOG_TRIVIAL ( info ) << "User wants to re-configure..." ;
p -> perform_updates ( std :: move ( updates ));
GUI :: ConfigWizard wizard ( nullptr , GUI :: ConfigWizard :: RR_DATA_INCOMPAT );
2018-10-01 15:09:31 +02:00
if ( ! wizard . run ( GUI :: wxGetApp (). preset_bundle , this )) {
2018-04-24 18:06:42 +02:00
return false ;
}
2018-10-01 15:09:31 +02:00
GUI :: wxGetApp (). load_current_presets ();
2018-04-24 18:06:42 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "User wants to exit Slic3r, bye..." ;
return false ;
}
}
else if ( updates . updates . size () > 0 ) {
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Update of %1% bundles available. Asking for confirmation ..." ) % updates . updates . size ();
std :: unordered_map < std :: string , std :: string > updates_map ;
for ( const auto & update : updates . updates ) {
auto vendor = update . name ();
auto ver_str = update . version . config_version . to_string ();
if ( ! update . version . comment . empty ()) {
ver_str += std :: string ( " (" ) + update . version . comment + ")" ;
}
2018-04-24 18:40:06 +02:00
updates_map . emplace ( std :: make_pair ( std :: move ( vendor ), std :: move ( ver_str )));
2018-04-24 18:06:42 +02:00
}
2018-06-22 15:27:04 +02:00
p -> had_config_update = true ; // Ditto, see above
2018-04-24 18:06:42 +02:00
GUI :: MsgUpdateConfig dlg ( std :: move ( updates_map ));
2018-04-12 20:04:48 +02:00
const auto res = dlg . ShowModal ();
2018-04-25 15:20:46 +02:00
if ( res == wxID_OK ) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "User agreed to perform the update" ;
2018-04-17 16:59:53 +02:00
p -> perform_updates ( std :: move ( updates ));
2018-06-06 10:52:19 +02:00
// Reload global configuration
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2018-10-01 15:09:31 +02:00
GUI :: wxGetApp (). preset_bundle -> load_presets ( * app_config );
GUI :: wxGetApp (). load_current_presets ();
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "User refused the update" ;
2018-04-11 13:12:08 +02:00
}
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "No configuration updates available." ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
return true ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
void PresetUpdater :: install_bundles_rsrc ( std :: vector < std :: string > bundles , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
Updates updates ;
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Installing %1% bundles from resources ..." ) % bundles . size ();
2018-04-16 16:52:11 +02:00
for ( const auto & bundle : bundles ) {
auto path_in_rsrc = p -> rsrc_path / bundle ;
auto path_in_vendors = p -> vendor_path / bundle ;
2018-04-24 18:06:42 +02:00
updates . updates . emplace_back ( std :: move ( path_in_rsrc ), std :: move ( path_in_vendors ), Version ());
2018-04-16 16:52:11 +02:00
}
2018-04-18 11:40:43 +02:00
p -> perform_updates ( std :: move ( updates ), snapshot );
2018-04-16 16:52:11 +02:00
}
2018-03-28 11:36:36 +02:00
}