lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / desktop / source / migration / migration.cxx
blob2173b01a881f576e0c622d2c9d6a7f1e8c92af58
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <algorithm>
23 #include <iterator>
24 #include <map>
25 #include <new>
26 #include <set>
28 #include <migration.hxx>
29 #include "migration_impl.hxx"
31 #include <sal/log.hxx>
32 #include <unotools/textsearch.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <unotools/bootstrap.hxx>
36 #include <rtl/bootstrap.hxx>
37 #include <rtl/uri.hxx>
38 #include <i18nlangtag/lang.h>
39 #include <tools/diagnose_ex.h>
40 #include <tools/urlobj.hxx>
41 #include <osl/file.hxx>
42 #include <osl/security.hxx>
43 #include <unotools/configmgr.hxx>
45 #include <com/sun/star/configuration/Update.hpp>
46 #include <com/sun/star/configuration/theDefaultProvider.hpp>
47 #include <com/sun/star/lang/XInitialization.hpp>
48 #include <com/sun/star/task/XJob.hpp>
49 #include <com/sun/star/beans/NamedValue.hpp>
50 #include <com/sun/star/beans/XPropertySet.hpp>
51 #include <com/sun/star/util/XRefreshable.hpp>
52 #include <com/sun/star/util/XChangesBatch.hpp>
53 #include <com/sun/star/util/XStringSubstitution.hpp>
54 #include <com/sun/star/embed/ElementModes.hpp>
55 #include <com/sun/star/embed/FileSystemStorageFactory.hpp>
56 #include <com/sun/star/embed/XStorage.hpp>
57 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
58 #include <com/sun/star/frame/theUICommandDescription.hpp>
59 #include <com/sun/star/ui/UIConfigurationManager.hpp>
60 #include <com/sun/star/ui/XUIConfiguration.hpp>
61 #include <com/sun/star/ui/XUIConfigurationStorage.hpp>
62 #include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
64 using namespace osl;
65 using namespace std;
66 using namespace com::sun::star::task;
67 using namespace com::sun::star::lang;
68 using namespace com::sun::star::beans;
69 using namespace com::sun::star::util;
70 using namespace com::sun::star::container;
71 using com::sun::star::uno::Exception;
72 using namespace com::sun::star;
75 namespace desktop
78 static const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL";
79 static const char ITEM_DESCRIPTOR_CONTAINER[] = "ItemDescriptorContainer";
80 static const char ITEM_DESCRIPTOR_LABEL[] = "Label";
82 static OUString retrieveLabelFromCommand(const OUString& sCommand, const OUString& sModuleIdentifier)
84 OUString sLabel;
86 uno::Reference< container::XNameAccess > xUICommands;
87 uno::Reference< container::XNameAccess > const xNameAccess(
88 frame::theUICommandDescription::get(
89 ::comphelper::getProcessComponentContext()) );
90 xNameAccess->getByName( sModuleIdentifier ) >>= xUICommands;
91 if (xUICommands.is()) {
92 if ( !sCommand.isEmpty() ) {
93 OUString aStr;
94 ::uno::Sequence< beans::PropertyValue > aPropSeq;
95 try {
96 uno::Any a( xUICommands->getByName( sCommand ));
97 if ( a >>= aPropSeq ) {
98 for ( sal_Int32 i = 0; i < aPropSeq.getLength(); i++ ) {
99 if ( aPropSeq[i].Name == "Label" ) {
100 aPropSeq[i].Value >>= aStr;
101 break;
106 sLabel = aStr;
107 } catch (const container::NoSuchElementException&) {
108 sLabel = sCommand;
109 sal_Int32 nIndex = sLabel.indexOf(':');
110 if (nIndex>=0 && nIndex <= sLabel.getLength()-1)
111 sLabel = sLabel.copy(nIndex+1);
117 return sLabel;
120 static OUString mapModuleShortNameToIdentifier(const OUString& sShortName)
122 OUString sIdentifier;
124 if ( sShortName == "StartModule" )
125 sIdentifier = "com.sun.star.frame.StartModule";
127 else if ( sShortName == "swriter" )
128 sIdentifier = "com.sun.star.text.TextDocument";
130 else if ( sShortName == "scalc" )
131 sIdentifier = "com.sun.star.sheet.SpreadsheetDocument";
133 else if ( sShortName == "sdraw" )
134 sIdentifier = "com.sun.star.drawing.DrawingDocument";
136 else if ( sShortName == "simpress" )
137 sIdentifier = "com.sun.star.presentation.PresentationDocument";
139 else if ( sShortName == "smath" )
140 sIdentifier = "com.sun.star.formula.FormulaProperties";
142 else if ( sShortName == "schart" )
143 sIdentifier = "com.sun.star.chart2.ChartDocument";
145 else if ( sShortName == "BasicIDE" )
146 sIdentifier = "com.sun.star.script.BasicIDE";
148 else if ( sShortName == "dbapp" )
149 sIdentifier = "com.sun.star.sdb.OfficeDatabaseDocument";
151 else if ( sShortName == "sglobal" )
152 sIdentifier = "com.sun.star.text.GlobalDocument";
154 else if ( sShortName == "sweb" )
155 sIdentifier = "com.sun.star.text.WebDocument";
157 else if ( sShortName == "swxform" )
158 sIdentifier = "com.sun.star.xforms.XMLFormDocument";
160 else if ( sShortName == "sbibliography" )
161 sIdentifier = "com.sun.star.frame.Bibliography";
163 return sIdentifier;
166 bool MigrationImpl::alreadyMigrated()
168 OUString const MIGRATION_STAMP_NAME("/MIGRATED4");
169 OUString aStr = m_aInfo.userdata + MIGRATION_STAMP_NAME;
170 File aFile(aStr);
171 // create migration stamp, and/or check its existence
172 bool bRet = aFile.open (osl_File_OpenFlag_Write | osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock) == FileBase::E_EXIST;
173 SAL_INFO( "desktop.migration", "File '" << aStr << "' exists? " << bRet );
174 return bRet;
177 bool MigrationImpl::initializeMigration()
179 bool bRet = false;
181 if (!checkMigrationCompleted()) {
182 readAvailableMigrations(m_vMigrationsAvailable);
183 sal_Int32 nIndex = findPreferredMigrationProcess(m_vMigrationsAvailable);
184 // m_aInfo is now set to the preferred migration source
185 if ( nIndex >= 0 ) {
186 if (alreadyMigrated())
187 return false;
188 m_vrMigrations = readMigrationSteps(m_vMigrationsAvailable[nIndex].name);
191 bRet = !m_aInfo.userdata.isEmpty();
194 SAL_INFO( "desktop.migration", "Migration " << ( bRet ? "needed" : "not required" ) );
196 return bRet;
199 void Migration::migrateSettingsIfNecessary()
201 MigrationImpl aImpl;
203 if (! aImpl.initializeMigration() )
204 return;
206 bool bResult = false;
207 try {
208 bResult = aImpl.doMigration();
209 } catch (const Exception&) {
210 TOOLS_WARN_EXCEPTION( "desktop", "doMigration()");
212 OSL_ENSURE(bResult, "Migration has not been successful");
215 MigrationImpl::MigrationImpl()
219 MigrationImpl::~MigrationImpl()
223 // The main entry point for migrating settings
224 bool MigrationImpl::doMigration()
226 // compile file list for migration
227 m_vrFileList = compileFileList();
229 bool result = false;
230 try {
231 NewVersionUIInfo aNewVersionUIInfo;
232 std::vector< MigrationModuleInfo > vModulesInfo = dectectUIChangesForAllModules();
233 aNewVersionUIInfo.init(vModulesInfo);
235 copyFiles();
237 const OUString sMenubarResourceURL("private:resource/menubar/menubar");
238 const OUString sToolbarResourcePre("private:resource/toolbar/");
239 for (MigrationModuleInfo & i : vModulesInfo) {
240 OUString sModuleIdentifier = mapModuleShortNameToIdentifier(i.sModuleShortName);
241 if (sModuleIdentifier.isEmpty())
242 continue;
245 OUString aOldCfgDataPath = m_aInfo.userdata + "/user/config/soffice.cfg/modules/" + i.sModuleShortName;
246 uno::Sequence< uno::Any > lArgs {uno::makeAny(aOldCfgDataPath), uno::makeAny(embed::ElementModes::READ)};
248 uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext());
249 uno::Reference< lang::XSingleServiceFactory > xStorageFactory(embed::FileSystemStorageFactory::create(xContext));
250 uno::Reference< embed::XStorage > xModules(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY);
251 uno::Reference< ui::XUIConfigurationManager2 > xOldCfgManager = ui::UIConfigurationManager::create(xContext);
253 if ( xModules.is() ) {
254 xOldCfgManager->setStorage( xModules );
255 xOldCfgManager->reload();
258 uno::Reference< ui::XUIConfigurationManager > xCfgManager = aNewVersionUIInfo.getConfigManager(i.sModuleShortName);
260 if (i.bHasMenubar) {
261 uno::Reference< container::XIndexContainer > xOldVersionMenuSettings(xOldCfgManager->getSettings(sMenubarResourceURL, true), uno::UNO_QUERY);
262 uno::Reference< container::XIndexContainer > xNewVersionMenuSettings = aNewVersionUIInfo.getNewMenubarSettings(i.sModuleShortName);
263 compareOldAndNewConfig(OUString(), xOldVersionMenuSettings, xNewVersionMenuSettings, sMenubarResourceURL);
264 mergeOldToNewVersion(xCfgManager, xNewVersionMenuSettings, sModuleIdentifier, sMenubarResourceURL);
267 sal_Int32 nToolbars = i.m_vToolbars.size();
268 if (nToolbars >0) {
269 for (sal_Int32 j=0; j<nToolbars; ++j) {
270 OUString sToolbarName = i.m_vToolbars[j];
271 OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName;
273 uno::Reference< container::XIndexContainer > xOldVersionToolbarSettings(xOldCfgManager->getSettings(sToolbarResourceURL, true), uno::UNO_QUERY);
274 uno::Reference< container::XIndexContainer > xNewVersionToolbarSettings = aNewVersionUIInfo.getNewToolbarSettings(i.sModuleShortName, sToolbarName);
275 compareOldAndNewConfig(OUString(), xOldVersionToolbarSettings, xNewVersionToolbarSettings, sToolbarResourceURL);
276 mergeOldToNewVersion(xCfgManager, xNewVersionToolbarSettings, sModuleIdentifier, sToolbarResourceURL);
280 m_aOldVersionItemsHashMap.clear();
283 // execute the migration items from Setup.xcu
284 copyConfig();
286 // execute custom migration services from Setup.xcu
287 // and refresh the cache
288 runServices();
289 uno::Reference< XRefreshable >(
290 configuration::theDefaultProvider::get(comphelper::getProcessComponentContext()),
291 uno::UNO_QUERY_THROW)->refresh();
293 result = true;
294 } catch (const css::uno::Exception &) {
295 TOOLS_WARN_EXCEPTION(
296 "desktop.migration",
297 "ignored Exception while migrating from version \"" << m_aInfo.productname
298 << "\" data \"" << m_aInfo.userdata << "\"");
301 // prevent running the migration multiple times
302 setMigrationCompleted();
303 return result;
306 void MigrationImpl::setMigrationCompleted()
308 try {
309 uno::Reference< XPropertySet > aPropertySet(getConfigAccess("org.openoffice.Setup/Office", true), uno::UNO_QUERY_THROW);
310 aPropertySet->setPropertyValue("MigrationCompleted", uno::makeAny(true));
311 uno::Reference< XChangesBatch >(aPropertySet, uno::UNO_QUERY_THROW)->commitChanges();
312 } catch (...) {
313 // fail silently
317 bool MigrationImpl::checkMigrationCompleted()
319 bool bMigrationCompleted = false;
320 try {
321 uno::Reference< XPropertySet > aPropertySet(
322 getConfigAccess("org.openoffice.Setup/Office"), uno::UNO_QUERY_THROW);
323 aPropertySet->getPropertyValue("MigrationCompleted") >>= bMigrationCompleted;
325 if( !bMigrationCompleted && getenv("SAL_DISABLE_USERMIGRATION" ) ) {
326 // migration prevented - fake its success
327 setMigrationCompleted();
328 bMigrationCompleted = true;
330 } catch (const Exception&) {
331 // just return false...
333 SAL_INFO( "desktop.migration", "Migration " << ( bMigrationCompleted ? "already completed" : "not done" ) );
335 return bMigrationCompleted;
338 static void insertSorted(migrations_available& rAvailableMigrations, supported_migration const & aSupportedMigration)
340 bool bInserted( false );
341 migrations_available::iterator pIter = rAvailableMigrations.begin();
342 while ( !bInserted && pIter != rAvailableMigrations.end()) {
343 if ( pIter->nPriority < aSupportedMigration.nPriority ) {
344 rAvailableMigrations.insert(pIter, aSupportedMigration );
345 bInserted = true;
346 break; // i111193: insert invalidates iterator!
348 ++pIter;
350 if ( !bInserted )
351 rAvailableMigrations.push_back( aSupportedMigration );
354 void MigrationImpl::readAvailableMigrations(migrations_available& rAvailableMigrations)
356 // get supported version names
357 uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_QUERY_THROW);
358 uno::Sequence< OUString > seqSupportedVersions = aMigrationAccess->getElementNames();
360 const OUString aVersionIdentifiers( "VersionIdentifiers" );
361 const OUString aPriorityIdentifier( "Priority" );
363 for (sal_Int32 i=0; i<seqSupportedVersions.getLength(); i++) {
364 sal_Int32 nPriority( 0 );
365 uno::Sequence< OUString > seqVersions;
366 uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(seqSupportedVersions[i]), uno::UNO_QUERY_THROW );
367 xMigrationData->getByName( aVersionIdentifiers ) >>= seqVersions;
368 xMigrationData->getByName( aPriorityIdentifier ) >>= nPriority;
370 supported_migration aSupportedMigration;
371 aSupportedMigration.name = seqSupportedVersions[i];
372 aSupportedMigration.nPriority = nPriority;
373 for (sal_Int32 j=0; j<seqVersions.getLength(); j++)
374 aSupportedMigration.supported_versions.push_back(seqVersions[j].trim());
375 insertSorted( rAvailableMigrations, aSupportedMigration );
376 SAL_INFO( "desktop.migration", " available migration '" << aSupportedMigration.name << "'" );
380 migrations_vr MigrationImpl::readMigrationSteps(const OUString& rMigrationName)
382 // get migration access
383 uno::Reference< XNameAccess > aMigrationAccess(getConfigAccess("org.openoffice.Setup/Migration/SupportedVersions"), uno::UNO_QUERY_THROW);
384 uno::Reference< XNameAccess > xMigrationData( aMigrationAccess->getByName(rMigrationName), uno::UNO_QUERY_THROW );
386 // get migration description from org.openoffice.Setup/Migration
387 // and build vector of migration steps
388 uno::Reference< XNameAccess > theNameAccess(xMigrationData->getByName("MigrationSteps"), uno::UNO_QUERY_THROW);
389 uno::Reference< XNameAccess > tmpAccess;
390 uno::Sequence< OUString > tmpSeq;
391 migrations_vr vrMigrations(new migrations_v);
392 for (const OUString& rMigrationStep : theNameAccess->getElementNames()) {
393 // get current migration step
394 theNameAccess->getByName(rMigrationStep) >>= tmpAccess;
395 migration_step tmpStep;
396 tmpStep.name = rMigrationStep;
398 // read included files from current step description
399 if (tmpAccess->getByName("IncludedFiles") >>= tmpSeq) {
400 for (const OUString& rSeqEntry : tmpSeq)
401 tmpStep.includeFiles.push_back(rSeqEntry);
404 // excluded files...
405 if (tmpAccess->getByName("ExcludedFiles") >>= tmpSeq) {
406 for (const OUString& rSeqEntry : tmpSeq)
407 tmpStep.excludeFiles.push_back(rSeqEntry);
410 // included nodes...
411 if (tmpAccess->getByName("IncludedNodes") >>= tmpSeq) {
412 for (const OUString& rSeqEntry : tmpSeq)
413 tmpStep.includeConfig.push_back(rSeqEntry);
416 // excluded nodes...
417 if (tmpAccess->getByName("ExcludedNodes") >>= tmpSeq) {
418 for (const OUString& rSeqEntry : tmpSeq)
419 tmpStep.excludeConfig.push_back(rSeqEntry);
422 // included extensions...
423 if (tmpAccess->getByName("IncludedExtensions") >>= tmpSeq) {
424 for (const OUString& rSeqEntry : tmpSeq)
425 tmpStep.includeExtensions.push_back(rSeqEntry);
428 // excluded extensions...
429 if (tmpAccess->getByName("ExcludedExtensions") >>= tmpSeq) {
430 for (const OUString& rSeqEntry : tmpSeq)
431 tmpStep.excludeExtensions.push_back(rSeqEntry);
434 // generic service
435 tmpAccess->getByName("MigrationService") >>= tmpStep.service;
437 vrMigrations->push_back(tmpStep);
439 return vrMigrations;
442 static FileBase::RC _checkAndCreateDirectory(INetURLObject const & dirURL)
444 FileBase::RC result = Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri));
445 if (result == FileBase::E_NOENT) {
446 INetURLObject baseURL(dirURL);
447 baseURL.removeSegment();
448 _checkAndCreateDirectory(baseURL);
449 return Directory::create(dirURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri));
450 } else
451 return result;
454 #if defined UNX && ! defined MACOSX
456 static const char XDG_CONFIG_PART[] = "/.config/";
458 OUString MigrationImpl::preXDGConfigDir(const OUString& rConfigDir)
460 OUString aPreXDGConfigPath;
461 const char* pXDGCfgHome = getenv("XDG_CONFIG_HOME");
463 // cater for XDG_CONFIG_HOME change
464 // If XDG_CONFIG_HOME is set then we;
465 // assume the user knows what they are doing ( room for improvement here, we could
466 // of course search the default config dir etc. also - but this is more complex,
467 // we would need to weigh results from the current config dir against matches in
468 // the 'old' config dir etc. ) - currently we just use the returned config dir.
469 // If XDG_CONFIG_HOME is NOT set;
470 // assume then we should now using the default $HOME/.config config location for
471 // our user profiles, however *all* previous libreoffice and openoffice.org
472 // configurations will be in the 'old' config directory and that's where we need
473 // to search - we convert the returned config dir to the 'old' dir
474 if ( !pXDGCfgHome && rConfigDir.endsWith( XDG_CONFIG_PART ) )
475 // remove trailing '.config/' but leave the terminating '/'
476 aPreXDGConfigPath = rConfigDir.copy( 0, rConfigDir.getLength() - sizeof( XDG_CONFIG_PART ) + 2 );
477 else
478 aPreXDGConfigPath = rConfigDir;
480 // the application-specific config dir is no longer prefixed by '.' because it is hidden under ".config"
481 // we have to add the '.' for the pre-XDG directory names
482 aPreXDGConfigPath += ".";
484 return aPreXDGConfigPath;
486 #endif
488 void MigrationImpl::setInstallInfoIfExist(
489 install_info& aInfo,
490 const OUString& rConfigDir,
491 const OUString& rVersion)
493 OUString url(INetURLObject(rConfigDir).GetMainURL(INetURLObject::DecodeMechanism::NONE));
494 osl::DirectoryItem item;
495 osl::FileStatus stat(osl_FileStatus_Mask_Type);
497 if (osl::DirectoryItem::get(url, item) == osl::FileBase::E_None
498 && item.getFileStatus(stat) == osl::FileBase::E_None
499 && stat.getFileType() == osl::FileStatus::Directory) {
500 aInfo.userdata = url;
501 aInfo.productname = rVersion;
505 install_info MigrationImpl::findInstallation(const strings_v& rVersions)
508 OUString aTopConfigDir;
509 osl::Security().getConfigDir( aTopConfigDir );
510 if ( !aTopConfigDir.isEmpty() && aTopConfigDir[ aTopConfigDir.getLength()-1 ] != '/' )
511 aTopConfigDir += "/";
513 #if defined UNX && ! defined MACOSX
514 OUString aPreXDGTopConfigDir = preXDGConfigDir(aTopConfigDir);
515 #endif
517 install_info aInfo;
518 for (auto const& elem : rVersions)
520 OUString aVersion, aProfileName;
521 sal_Int32 nSeparatorIndex = elem.indexOf('=');
522 if ( nSeparatorIndex != -1 ) {
523 aVersion = elem.copy( 0, nSeparatorIndex );
524 aProfileName = elem.copy( nSeparatorIndex+1 );
527 if ( !aVersion.isEmpty() && !aProfileName.isEmpty() &&
528 ( aInfo.userdata.isEmpty() ||
529 aProfileName.equalsIgnoreAsciiCase(
530 utl::ConfigManager::getProductName() ) ) ) {
531 setInstallInfoIfExist(aInfo, aTopConfigDir + aProfileName, aVersion);
532 #if defined UNX && ! defined MACOSX
533 //try preXDG path if the new one does not exist
534 if ( aInfo.userdata.isEmpty())
535 setInstallInfoIfExist(aInfo, aPreXDGTopConfigDir + aProfileName, aVersion);
536 #endif
540 return aInfo;
543 sal_Int32 MigrationImpl::findPreferredMigrationProcess(const migrations_available& rAvailableMigrations)
545 sal_Int32 nIndex( -1 );
546 sal_Int32 i( 0 );
548 for (auto const& availableMigration : rAvailableMigrations)
550 install_info aInstallInfo = findInstallation(availableMigration.supported_versions);
551 if (!aInstallInfo.productname.isEmpty() ) {
552 m_aInfo = aInstallInfo;
553 nIndex = i;
554 break;
556 ++i;
559 SAL_INFO( "desktop.migration", " preferred migration is from product '" << m_aInfo.productname << "'");
560 SAL_INFO( "desktop.migration", " and settings directory '" << m_aInfo.userdata << "'");
562 return nIndex;
565 strings_vr MigrationImpl::applyPatterns(const strings_v& vSet, const strings_v& vPatterns)
567 using namespace utl;
568 strings_vr vrResult(new strings_v);
569 for (auto const& pattern : vPatterns)
571 // find matches for this pattern in input set
572 // and copy them to the result
573 SearchParam param(pattern, SearchParam::SearchType::Regexp);
574 TextSearch ts(param, LANGUAGE_DONTKNOW);
575 sal_Int32 start = 0;
576 sal_Int32 end = 0;
577 for (auto const& elem : vSet)
579 end = elem.getLength();
580 if (ts.SearchForward(elem, &start, &end))
581 vrResult->push_back(elem);
584 return vrResult;
587 strings_vr MigrationImpl::getAllFiles(const OUString& baseURL) const
589 strings_vr vrResult(new strings_v);
591 // get sub dirs
592 Directory dir(baseURL);
593 if (dir.open() == FileBase::E_None) {
594 strings_v vSubDirs;
595 strings_vr vrSubResult;
597 // work through directory contents...
598 DirectoryItem item;
599 FileStatus fs(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL);
600 while (dir.getNextItem(item) == FileBase::E_None) {
601 if (item.getFileStatus(fs) == FileBase::E_None) {
602 if (fs.getFileType() == FileStatus::Directory)
603 vSubDirs.push_back(fs.getFileURL());
604 else
605 vrResult->push_back(fs.getFileURL());
609 // recurse subfolders
610 for (auto const& subDir : vSubDirs)
612 vrSubResult = getAllFiles(subDir);
613 vrResult->insert(vrResult->end(), vrSubResult->begin(), vrSubResult->end());
616 return vrResult;
619 namespace
622 // removes elements of vector 2 in vector 1
623 strings_v subtract(strings_v const & va, strings_v const & vb)
625 strings_v a(va);
626 std::sort(a.begin(), a.end());
627 strings_v::iterator ae(std::unique(a.begin(), a.end()));
628 strings_v b(vb);
629 std::sort(b.begin(), b.end());
630 strings_v::iterator be(std::unique(b.begin(), b.end()));
631 strings_v c;
632 std::set_difference(a.begin(), ae, b.begin(), be, std::back_inserter(c));
633 return c;
638 strings_vr MigrationImpl::compileFileList()
641 strings_vr vrResult(new strings_v);
642 strings_vr vrInclude;
643 strings_vr vrExclude;
645 // get a list of all files:
646 strings_vr vrFiles = getAllFiles(m_aInfo.userdata);
648 // get a file list result for each migration step
649 for (auto const& rMigration : *m_vrMigrations)
651 vrInclude = applyPatterns(*vrFiles, rMigration.includeFiles);
652 vrExclude = applyPatterns(*vrFiles, rMigration.excludeFiles);
653 strings_v sub(subtract(*vrInclude, *vrExclude));
654 vrResult->insert(vrResult->end(), sub.begin(), sub.end());
656 return vrResult;
659 namespace
662 struct componentParts {
663 std::set< OUString > includedPaths;
664 std::set< OUString > excludedPaths;
667 typedef std::map< OUString, componentParts > Components;
669 bool getComponent(OUString const & path, OUString * component)
671 OSL_ASSERT(component != nullptr);
672 if (path.isEmpty() || path[0] != '/') {
673 SAL_INFO( "desktop.migration", "configuration migration in/exclude path " << path << " ignored (does not start with slash)" );
674 return false;
676 sal_Int32 i = path.indexOf('/', 1);
677 *component = i < 0 ? path.copy(1) : path.copy(1, i - 1);
678 return true;
683 void MigrationImpl::copyConfig()
685 Components comps;
686 for (auto const& rMigrationStep : *m_vrMigrations) {
687 for (const OUString& rIncludePath : rMigrationStep.includeConfig) {
688 OUString comp;
689 if (getComponent(rIncludePath, &comp)) {
690 comps[comp].includedPaths.insert(rIncludePath);
693 for (const OUString& rExcludePath : rMigrationStep.excludeConfig) {
694 OUString comp;
695 if (getComponent(rExcludePath, &comp)) {
696 comps[comp].excludedPaths.insert(rExcludePath);
701 // check if the shared registrymodifications.xcu file exists
702 bool bRegistryModificationsXcuExists = false;
703 OUString regFilePath(m_aInfo.userdata);
704 regFilePath += "/user/registrymodifications.xcu";
705 File regFile(regFilePath);
706 ::osl::FileBase::RC nError = regFile.open(osl_File_OpenFlag_Read);
707 if ( nError == ::osl::FileBase::E_None ) {
708 bRegistryModificationsXcuExists = true;
709 regFile.close();
712 for (auto const& comp : comps)
714 if (!comp.second.includedPaths.empty()) {
715 if (!bRegistryModificationsXcuExists) {
716 // shared registrymodifications.xcu does not exists
717 // the configuration is split in many registry files
718 // determine the file names from the first element in included paths
719 OUStringBuffer buf(m_aInfo.userdata);
720 buf.append("/user/registry/data");
721 sal_Int32 n = 0;
722 do {
723 OUString seg(comp.first.getToken(0, '.', n));
724 OUString enc(
725 rtl::Uri::encode(
726 seg, rtl_UriCharClassPchar, rtl_UriEncodeStrict,
727 RTL_TEXTENCODING_UTF8));
728 if (enc.isEmpty() && !seg.isEmpty()) {
729 SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (cannot be encoded as file path)" );
730 goto next;
732 buf.append('/');
733 buf.append(enc);
734 } while (n >= 0);
735 buf.append(".xcu");
736 regFilePath = buf.toString();
738 configuration::Update::get(
739 comphelper::getProcessComponentContext())->
740 insertModificationXcuFile(
741 regFilePath,
742 comphelper::containerToSequence(comp.second.includedPaths),
743 comphelper::containerToSequence(comp.second.excludedPaths));
744 } else {
745 SAL_INFO( "desktop.migration", "configuration migration component " << comp.first << " ignored (only excludes, no includes)" );
747 next:
752 uno::Reference< XNameAccess > MigrationImpl::getConfigAccess(const sal_Char* pPath, bool bUpdate)
754 uno::Reference< XNameAccess > xNameAccess;
755 try {
756 OUString sAccessSrvc;
757 if (bUpdate)
758 sAccessSrvc = "com.sun.star.configuration.ConfigurationUpdateAccess";
759 else
760 sAccessSrvc = "com.sun.star.configuration.ConfigurationAccess";
762 OUString sConfigURL = OUString::createFromAscii(pPath);
764 uno::Reference< XMultiServiceFactory > theConfigProvider(
765 configuration::theDefaultProvider::get(
766 comphelper::getProcessComponentContext()));
768 // access the provider
769 uno::Sequence< uno::Any > theArgs {uno::makeAny(sConfigURL)};
770 xNameAccess.set(
771 theConfigProvider->createInstanceWithArguments(
772 sAccessSrvc, theArgs ), uno::UNO_QUERY_THROW );
773 } catch (const css::uno::Exception&) {
774 TOOLS_WARN_EXCEPTION("desktop.migration", "ignoring");
776 return xNameAccess;
779 void MigrationImpl::copyFiles()
781 OUString localName;
782 OUString destName;
783 OUString userInstall;
784 utl::Bootstrap::PathStatus aStatus;
785 aStatus = utl::Bootstrap::locateUserInstallation(userInstall);
786 if (aStatus == utl::Bootstrap::PATH_EXISTS) {
787 for (auto const& rFile : *m_vrFileList)
789 // remove installation prefix from file
790 localName = rFile.copy(m_aInfo.userdata.getLength());
791 if (localName.endsWith( "/autocorr/acor_.dat")) {
792 // Previous versions used an empty language tag for
793 // LANGUAGE_DONTKNOW with the "[All]" autocorrection entry.
794 // As of LibreOffice 4.0 it is 'und' for LANGUAGE_UNDETERMINED
795 // so the file name is "acor_und.dat".
796 localName = localName.copy( 0, localName.getLength() - 4) + "und.dat";
798 destName = userInstall + localName;
799 INetURLObject aURL(destName);
800 // check whether destination directory exists
801 aURL.removeSegment();
802 _checkAndCreateDirectory(aURL);
803 FileBase::RC copyResult = File::copy(rFile, destName);
804 if (copyResult != FileBase::E_None) {
805 SAL_WARN( "desktop", "Cannot copy " << rFile << " to " << destName);
808 } else {
809 OSL_FAIL("copyFiles: UserInstall does not exist");
813 void MigrationImpl::runServices()
815 // Build argument array
816 uno::Sequence< uno::Any > seqArguments(3);
817 seqArguments[0] <<= NamedValue("Productname",
818 uno::makeAny(m_aInfo.productname));
819 seqArguments[1] <<= NamedValue("UserData",
820 uno::makeAny(m_aInfo.userdata));
823 // create an instance of every migration service
824 // and execute the migration job
825 uno::Reference< XJob > xMigrationJob;
827 uno::Reference< uno::XComponentContext > xContext(comphelper::getProcessComponentContext());
828 for (auto const& rMigration : *m_vrMigrations)
830 if( !rMigration.service.isEmpty()) {
832 try {
833 // set black list for extension migration
834 uno::Sequence< OUString > seqExtBlackList;
835 sal_uInt32 nSize = rMigration.excludeExtensions.size();
836 if ( nSize > 0 )
837 seqExtBlackList = comphelper::arrayToSequence< OUString >(
838 &rMigration.excludeExtensions[0], nSize );
839 seqArguments[2] <<= NamedValue("ExtensionBlackList",
840 uno::makeAny( seqExtBlackList ));
842 xMigrationJob.set(
843 xContext->getServiceManager()->createInstanceWithArgumentsAndContext(rMigration.service, seqArguments, xContext),
844 uno::UNO_QUERY_THROW);
846 xMigrationJob->execute(uno::Sequence< NamedValue >());
849 } catch (const Exception&) {
850 TOOLS_WARN_EXCEPTION( "desktop", "Execution of migration service failed. Service: "
851 << rMigration.service);
852 } catch (...) {
853 SAL_WARN( "desktop", "Execution of migration service failed (Exception caught).\nService: "
854 << rMigration.service << "\nNo message available");
861 std::vector< MigrationModuleInfo > MigrationImpl::dectectUIChangesForAllModules() const
863 std::vector< MigrationModuleInfo > vModulesInfo;
864 const OUString MENUBAR("menubar");
865 const OUString TOOLBAR("toolbar");
867 uno::Sequence< uno::Any > lArgs {uno::makeAny(m_aInfo.userdata + "/user/config/soffice.cfg/modules"),
868 uno::makeAny(embed::ElementModes::READ)};
870 uno::Reference< lang::XSingleServiceFactory > xStorageFactory(
871 embed::FileSystemStorageFactory::create(comphelper::getProcessComponentContext()));
872 uno::Reference< embed::XStorage > xModules;
874 xModules.set(xStorageFactory->createInstanceWithArguments(lArgs), uno::UNO_QUERY);
875 if (!xModules.is())
876 return vModulesInfo;
878 uno::Reference< container::XNameAccess > xAccess(xModules, uno::UNO_QUERY);
879 uno::Sequence< OUString > lNames = xAccess->getElementNames();
880 sal_Int32 nLength = lNames.getLength();
881 for (sal_Int32 i=0; i<nLength; ++i) {
882 OUString sModuleShortName = lNames[i];
883 uno::Reference< embed::XStorage > xModule = xModules->openStorageElement(sModuleShortName, embed::ElementModes::READ);
884 if (xModule.is()) {
885 MigrationModuleInfo aModuleInfo;
887 uno::Reference< embed::XStorage > xMenubar = xModule->openStorageElement(MENUBAR, embed::ElementModes::READ);
888 if (xMenubar.is()) {
889 uno::Reference< container::XNameAccess > xNameAccess(xMenubar, uno::UNO_QUERY);
890 if (xNameAccess->getElementNames().getLength() > 0) {
891 aModuleInfo.sModuleShortName = sModuleShortName;
892 aModuleInfo.bHasMenubar = true;
896 uno::Reference< embed::XStorage > xToolbar = xModule->openStorageElement(TOOLBAR, embed::ElementModes::READ);
897 if (xToolbar.is()) {
898 const OUString RESOURCEURL_CUSTOM_ELEMENT("custom_");
899 sal_Int32 nCustomLen = 7;
901 uno::Reference< container::XNameAccess > xNameAccess(xToolbar, uno::UNO_QUERY);
902 ::uno::Sequence< OUString > lToolbars = xNameAccess->getElementNames();
903 for (sal_Int32 j=0; j<lToolbars.getLength(); ++j) {
904 OUString sToolbarName = lToolbars[j];
905 if (sToolbarName.getLength()>=nCustomLen &&
906 sToolbarName.copy(0, nCustomLen) == RESOURCEURL_CUSTOM_ELEMENT)
907 continue;
909 aModuleInfo.sModuleShortName = sModuleShortName;
910 sal_Int32 nIndex = sToolbarName.lastIndexOf('.');
911 if (nIndex > 0) {
912 OUString sExtension(sToolbarName.copy(nIndex));
913 OUString sToolbarResourceName(sToolbarName.copy(0, nIndex));
914 if (!sToolbarResourceName.isEmpty() && sExtension == ".xml")
915 aModuleInfo.m_vToolbars.push_back(sToolbarResourceName);
920 if (!aModuleInfo.sModuleShortName.isEmpty())
921 vModulesInfo.push_back(aModuleInfo);
925 return vModulesInfo;
928 void MigrationImpl::compareOldAndNewConfig(const OUString& sParent,
929 const uno::Reference< container::XIndexContainer >& xIndexOld,
930 const uno::Reference< container::XIndexContainer >& xIndexNew,
931 const OUString& sResourceURL)
933 const OUString MENU_SEPARATOR(" | ");
935 std::vector< MigrationItem > vOldItems;
936 std::vector< MigrationItem > vNewItems;
937 uno::Sequence< beans::PropertyValue > aProp;
938 sal_Int32 nOldCount = xIndexOld->getCount();
939 sal_Int32 nNewCount = xIndexNew->getCount();
941 for (int n=0; n<nOldCount; ++n) {
942 MigrationItem aMigrationItem;
943 if (xIndexOld->getByIndex(n) >>= aProp) {
944 for(int i=0; i<aProp.getLength(); ++i) {
945 if ( aProp[i].Name == ITEM_DESCRIPTOR_COMMANDURL )
946 aProp[i].Value >>= aMigrationItem.m_sCommandURL;
947 else if ( aProp[i].Name == ITEM_DESCRIPTOR_CONTAINER )
948 aProp[i].Value >>= aMigrationItem.m_xPopupMenu;
951 if (!aMigrationItem.m_sCommandURL.isEmpty())
952 vOldItems.push_back(aMigrationItem);
956 for (int n=0; n<nNewCount; ++n) {
957 MigrationItem aMigrationItem;
958 if (xIndexNew->getByIndex(n) >>= aProp) {
959 for(int i=0; i<aProp.getLength(); ++i) {
960 if ( aProp[i].Name == ITEM_DESCRIPTOR_COMMANDURL )
961 aProp[i].Value >>= aMigrationItem.m_sCommandURL;
962 else if ( aProp[i].Name == ITEM_DESCRIPTOR_CONTAINER )
963 aProp[i].Value >>= aMigrationItem.m_xPopupMenu;
966 if (!aMigrationItem.m_sCommandURL.isEmpty())
967 vNewItems.push_back(aMigrationItem);
971 OUString sSibling;
972 for (auto const& oldItem : vOldItems)
974 std::vector< MigrationItem >::iterator pFound = std::find(vNewItems.begin(), vNewItems.end(), oldItem);
975 if (pFound != vNewItems.end() && oldItem.m_xPopupMenu.is()) {
976 OUString sName;
977 if (!sParent.isEmpty())
978 sName = sParent + MENU_SEPARATOR + oldItem.m_sCommandURL;
979 else
980 sName = oldItem.m_sCommandURL;
981 compareOldAndNewConfig(sName, oldItem.m_xPopupMenu, pFound->m_xPopupMenu, sResourceURL);
982 } else if (pFound == vNewItems.end()) {
983 MigrationItem aMigrationItem(sParent, sSibling, oldItem.m_sCommandURL, oldItem.m_xPopupMenu);
984 if (m_aOldVersionItemsHashMap.find(sResourceURL)==m_aOldVersionItemsHashMap.end()) {
985 std::vector< MigrationItem > vMigrationItems;
986 m_aOldVersionItemsHashMap.emplace(sResourceURL, vMigrationItems);
987 m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem);
988 } else {
989 if (std::find(m_aOldVersionItemsHashMap[sResourceURL].begin(), m_aOldVersionItemsHashMap[sResourceURL].end(), aMigrationItem)==m_aOldVersionItemsHashMap[sResourceURL].end())
990 m_aOldVersionItemsHashMap[sResourceURL].push_back(aMigrationItem);
994 sSibling = oldItem.m_sCommandURL;
998 void MigrationImpl::mergeOldToNewVersion(const uno::Reference< ui::XUIConfigurationManager >& xCfgManager,
999 const uno::Reference< container::XIndexContainer>& xIndexContainer,
1000 const OUString& sModuleIdentifier,
1001 const OUString& sResourceURL)
1003 MigrationHashMap::iterator pFound = m_aOldVersionItemsHashMap.find(sResourceURL);
1004 if (pFound==m_aOldVersionItemsHashMap.end())
1005 return;
1007 for (auto const& elem : pFound->second)
1009 uno::Reference< container::XIndexContainer > xTemp = xIndexContainer;
1011 OUString sParentNodeName = elem.m_sParentNodeName;
1012 sal_Int32 nIndex = 0;
1013 do {
1014 OUString sToken = sParentNodeName.getToken(0, '|', nIndex).trim();
1015 if (sToken.isEmpty())
1016 break;
1018 sal_Int32 nCount = xTemp->getCount();
1019 for (sal_Int32 i=0; i<nCount; ++i) {
1020 OUString sCommandURL;
1021 OUString sLabel;
1022 uno::Reference< container::XIndexContainer > xChild;
1024 uno::Sequence< beans::PropertyValue > aPropSeq;
1025 xTemp->getByIndex(i) >>= aPropSeq;
1026 for (sal_Int32 j=0; j<aPropSeq.getLength(); ++j) {
1027 OUString sPropName = aPropSeq[j].Name;
1028 if ( sPropName == ITEM_DESCRIPTOR_COMMANDURL )
1029 aPropSeq[j].Value >>= sCommandURL;
1030 else if ( sPropName == ITEM_DESCRIPTOR_LABEL )
1031 aPropSeq[j].Value >>= sLabel;
1032 else if ( sPropName == ITEM_DESCRIPTOR_CONTAINER )
1033 aPropSeq[j].Value >>= xChild;
1036 if (sCommandURL == sToken) {
1037 xTemp = xChild;
1038 break;
1042 } while (nIndex >= 0);
1044 if (nIndex == -1) {
1045 uno::Sequence< beans::PropertyValue > aPropSeq {
1046 beans::PropertyValue(ITEM_DESCRIPTOR_COMMANDURL, 0, uno::makeAny(elem.m_sCommandURL), beans::PropertyState_DIRECT_VALUE),
1047 beans::PropertyValue(ITEM_DESCRIPTOR_LABEL, 0, uno::makeAny(retrieveLabelFromCommand(elem.m_sCommandURL, sModuleIdentifier)), beans::PropertyState_DIRECT_VALUE),
1048 beans::PropertyValue(ITEM_DESCRIPTOR_CONTAINER, 0, uno::makeAny(elem.m_xPopupMenu), beans::PropertyState_DIRECT_VALUE)
1051 if (elem.m_sPrevSibling.isEmpty())
1052 xTemp->insertByIndex(0, uno::makeAny(aPropSeq));
1053 else {
1054 sal_Int32 nCount = xTemp->getCount();
1055 sal_Int32 i = 0;
1056 for (; i<nCount; ++i) {
1057 OUString sCmd;
1058 uno::Sequence< beans::PropertyValue > aTempPropSeq;
1059 xTemp->getByIndex(i) >>= aTempPropSeq;
1060 for (sal_Int32 j=0; j<aTempPropSeq.getLength(); ++j) {
1061 if ( aTempPropSeq[j].Name == ITEM_DESCRIPTOR_COMMANDURL ) {
1062 aTempPropSeq[j].Value >>= sCmd;
1063 break;
1067 if (sCmd == elem.m_sPrevSibling)
1068 break;
1071 xTemp->insertByIndex(i+1, uno::makeAny(aPropSeq));
1076 uno::Reference< container::XIndexAccess > xIndexAccess(xIndexContainer, uno::UNO_QUERY);
1077 if (xIndexAccess.is())
1078 xCfgManager->replaceSettings(sResourceURL, xIndexAccess);
1080 uno::Reference< ui::XUIConfigurationPersistence > xUIConfigurationPersistence(xCfgManager, uno::UNO_QUERY);
1081 if (xUIConfigurationPersistence.is())
1082 xUIConfigurationPersistence->store();
1085 uno::Reference< ui::XUIConfigurationManager > NewVersionUIInfo::getConfigManager(const OUString& sModuleShortName) const
1087 uno::Reference< ui::XUIConfigurationManager > xCfgManager;
1089 for ( const css::beans::PropertyValue& rProp : m_lCfgManagerSeq) {
1090 if (rProp.Name == sModuleShortName) {
1091 rProp.Value >>= xCfgManager;
1092 break;
1096 return xCfgManager;
1099 uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewMenubarSettings(const OUString& sModuleShortName) const
1101 uno::Reference< container::XIndexContainer > xNewMenuSettings;
1103 for (sal_Int32 i=0; i<m_lNewVersionMenubarSettingsSeq.getLength(); ++i) {
1104 if (m_lNewVersionMenubarSettingsSeq[i].Name == sModuleShortName) {
1105 m_lNewVersionMenubarSettingsSeq[i].Value >>= xNewMenuSettings;
1106 break;
1110 return xNewMenuSettings;
1113 uno::Reference< container::XIndexContainer > NewVersionUIInfo::getNewToolbarSettings(const OUString& sModuleShortName, const OUString& sToolbarName) const
1115 uno::Reference< container::XIndexContainer > xNewToolbarSettings;
1117 for (sal_Int32 i=0; i<m_lNewVersionToolbarSettingsSeq.getLength(); ++i) {
1118 if (m_lNewVersionToolbarSettingsSeq[i].Name == sModuleShortName) {
1119 uno::Sequence< beans::PropertyValue > lToolbarSettingsSeq;
1120 m_lNewVersionToolbarSettingsSeq[i].Value >>= lToolbarSettingsSeq;
1121 for (sal_Int32 j=0; j<lToolbarSettingsSeq.getLength(); ++j) {
1122 if (lToolbarSettingsSeq[j].Name == sToolbarName) {
1123 lToolbarSettingsSeq[j].Value >>= xNewToolbarSettings;
1124 break;
1128 break;
1132 return xNewToolbarSettings;
1135 void NewVersionUIInfo::init(const std::vector< MigrationModuleInfo >& vModulesInfo)
1137 m_lCfgManagerSeq.resize(vModulesInfo.size());
1138 m_lNewVersionMenubarSettingsSeq.realloc(vModulesInfo.size());
1139 m_lNewVersionToolbarSettingsSeq.realloc(vModulesInfo.size());
1141 const OUString sMenubarResourceURL("private:resource/menubar/menubar");
1142 const OUString sToolbarResourcePre("private:resource/toolbar/");
1144 uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier = ui::theModuleUIConfigurationManagerSupplier::get( ::comphelper::getProcessComponentContext() );
1146 for (size_t i=0; i<vModulesInfo.size(); ++i) {
1147 OUString sModuleIdentifier = mapModuleShortNameToIdentifier(vModulesInfo[i].sModuleShortName);
1148 if (!sModuleIdentifier.isEmpty()) {
1149 uno::Reference< ui::XUIConfigurationManager > xCfgManager = xModuleCfgSupplier->getUIConfigurationManager(sModuleIdentifier);
1150 m_lCfgManagerSeq[i].Name = vModulesInfo[i].sModuleShortName;
1151 m_lCfgManagerSeq[i].Value <<= xCfgManager;
1153 if (vModulesInfo[i].bHasMenubar) {
1154 m_lNewVersionMenubarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName;
1155 m_lNewVersionMenubarSettingsSeq[i].Value <<= xCfgManager->getSettings(sMenubarResourceURL, true);
1158 sal_Int32 nToolbars = vModulesInfo[i].m_vToolbars.size();
1159 if (nToolbars > 0) {
1160 uno::Sequence< beans::PropertyValue > lPropSeq(nToolbars);
1161 for (sal_Int32 j=0; j<nToolbars; ++j) {
1162 OUString sToolbarName = vModulesInfo[i].m_vToolbars[j];
1163 OUString sToolbarResourceURL = sToolbarResourcePre + sToolbarName;
1165 lPropSeq[j].Name = sToolbarName;
1166 lPropSeq[j].Value <<= xCfgManager->getSettings(sToolbarResourceURL, true);
1169 m_lNewVersionToolbarSettingsSeq[i].Name = vModulesInfo[i].sModuleShortName;
1170 m_lNewVersionToolbarSettingsSeq[i].Value <<= lPropSeq;
1176 } // namespace desktop
1178 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */