Example #1
0
        public virtual int UpgradeProject(
            string projectFilePath,
            uint upgradeFlag,
            string initialCopyLocation,
            out string upgradeFullyQualifiedFileName,
            IVsUpgradeLogger upgradeLogger,
            out int upgradeRequired,
            out Guid newProjectFactory
            )
        {
            // initialize out params in case of failure
            upgradeFullyQualifiedFileName = null;
            upgradeRequired   = 0;
            newProjectFactory = Guid.Empty;

            __VSPPROJECTUPGRADEVIAFACTORYFLAGS flag;
            string copyLocation = initialCopyLocation;
            var    r            = NormalizeUpgradeFlag(upgradeFlag, out flag, ref copyLocation);

            if (r != VSConstants.S_OK)
            {
                return(r);
            }

            string projectName = Path.GetFileNameWithoutExtension(projectFilePath);
            var    logger      = new ProjectUpgradeLogger(upgradeLogger, projectName, projectFilePath);

            uint ignore;

            ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore);

            // no upgrade required and not 'copy-backup'
            if (upgradeRequired == 0 && !HasCopyBackupFlag(flag))
            {
                //Write an informational message "No upgrade required for project foo"?
                logger.LogInfo(SR.GetString(SR.ProjectConversionNotRequired));
                logger.LogInfo(SR.GetString(SR.ConversionNotRequired));

                upgradeFullyQualifiedFileName = projectFilePath;
                return(VSConstants.S_OK);
            }

            // upgrade is not required but backup may still be needed

            var projectFileName = Path.GetFileName(projectFilePath);

            upgradeFullyQualifiedFileName = projectFilePath;

            if (HasSxSBackupFlag(flag))
            {
                // for SxS call use location near the original file
                copyLocation = Path.GetDirectoryName(projectFilePath);
            }

            // workflow is taken from vsprjfactory.cpp (vsproject\vsproject)
            // 1. convert the project (in-memory)
            // 2. save SCC related info
            // 3. use data stored on step 2 in GetSccInfo calls (during QueryEditFiles)
            // 4. if succeeded - save project on disk
            // F# doesn't use .user file so all related code is skipped
            try
            {
                // load MSBuild project in memory: this will be needed in all cases not depending whether upgrade is required or not
                // we use this project to determine the set of files to copy
                ProjectRootElement convertedProject = ConvertProject(projectFilePath, logger);
                if (convertedProject == null)
                {
                    throw new ProjectUpgradeFailedException();
                }

                // OK, we need upgrade, save SCC info and ask if project file can be edited
                if (upgradeRequired != 0)
                {
                    // 2. save SCC related info
                    this.m_lastUpgradedProjectFile = projectFilePath;
                    foreach (var property in convertedProject.Properties)
                    {
                        switch (property.Name)
                        {
                        case SCC_LOCAL_PATH:
                            this.m_sccLocalPath = property.Value;
                            break;

                        case SCC_AUX_PATH:
                            this.m_sccAuxPath = property.Value;
                            break;

                        case SCC_PROVIDER:
                            this.m_sccProvider = property.Value;
                            break;

                        case SCC_PROJECT_NAME:
                            this.m_sccProjectName = property.Value;
                            break;

                        default:
                            break;
                        }
                    }

                    // 3. Query for edit (this call may query information stored on previous step)
                    IVsQueryEditQuerySave2 queryEdit = site.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2;
                    if (queryEdit != null)
                    {
                        uint editVerdict;
                        uint queryEditMoreInfo;
                        const tagVSQueryEditFlags tagVSQueryEditFlags_QEF_AllowUnopenedProjects = (tagVSQueryEditFlags)0x80;

                        int hr = queryEdit.QueryEditFiles(
                            (uint)(tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting | tagVSQueryEditFlags.QEF_DisallowInMemoryEdits | tagVSQueryEditFlags_QEF_AllowUnopenedProjects),
                            1, new[] { projectFilePath }, null, null, out editVerdict, out queryEditMoreInfo);

                        if (ErrorHandler.Failed(hr))
                        {
                            throw new ProjectUpgradeFailedException();
                        }

                        if (editVerdict != (uint)tagVSQueryEditResult.QER_EditOK)
                        {
                            throw new ProjectUpgradeFailedException(SR.GetString(SR.UpgradeCannotOpenProjectFileForEdit));
                        }

                        // If file was modified during the checkout, maybe upgrade is not needed
                        if ((queryEditMoreInfo & (uint)tagVSQueryEditResultFlags.QER_MaybeChanged) != 0)
                        {
                            ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore);
                            if (upgradeRequired == 0)
                            {
                                if (logger != null)
                                {
                                    logger.LogInfo(SR.GetString(SR.UpgradeNoNeedToUpgradeAfterCheckout));
                                }

                                return(VSConstants.S_OK);
                            }
                        }
                    }
                }

                // 3.1 copy backup
                BackupProjectFilesIfNeeded(projectFilePath, logger, flag, copyLocation, convertedProject);

                // 4. if we have performed upgrade - save project to disk
                if (upgradeRequired != 0)
                {
                    try
                    {
                        convertedProject.Save(projectFilePath);
                    }
                    catch (Exception ex)
                    {
                        throw new ProjectUpgradeFailedException(ex.Message, ex);
                    }
                }
                // 821083: "Converted" should NOT be localized - it is referenced in the XSLT used to display the UpgradeReport
                logger.LogStatus("Converted");
            }
            catch (ProjectUpgradeFailedException ex)
            {
                var exception = ex.InnerException ?? ex;

                if (exception != null && !string.IsNullOrEmpty(exception.Message))
                {
                    logger.LogError(exception.Message);
                }

                upgradeFullyQualifiedFileName = "";
                m_lastUpgradedProjectFile     = null;
                return(VSConstants.E_FAIL);
            }
            return(VSConstants.S_OK);
        }
        public virtual int UpgradeProject(string projectFileName, uint upgradeFlag, string copyLocation, out string upgradeFullyQualifiedFileName, IVsUpgradeLogger logger, out int upgradeRequired,
                                          out Guid newProjectFactory)
        {
            uint ignore;

            this.UpgradeProject_CheckOnly(projectFileName, logger, out upgradeRequired, out newProjectFactory, out ignore);
            if (upgradeRequired == 0)
            {
                upgradeFullyQualifiedFileName = projectFileName;
                return(VSConstants.S_OK);
            }

            string projectName = Path.GetFileNameWithoutExtension(projectFileName);

            upgradeFullyQualifiedFileName = projectFileName;

            // Query for edit
            IVsQueryEditQuerySave2 queryEdit = site.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2;

            if (queryEdit != null)
            {
                uint editVerdict;
                uint queryEditMoreInfo;
                const tagVSQueryEditFlags tagVSQueryEditFlags_QEF_AllowUnopenedProjects = (tagVSQueryEditFlags)0x80;

                int hr = queryEdit.QueryEditFiles(
                    (uint)(tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting | tagVSQueryEditFlags.QEF_DisallowInMemoryEdits | tagVSQueryEditFlags_QEF_AllowUnopenedProjects),
                    1, new[] { projectFileName }, null, null, out editVerdict, out queryEditMoreInfo);
                if (ErrorHandler.Failed(hr))
                {
                    return(VSConstants.E_FAIL);
                }

                if (editVerdict != (uint)tagVSQueryEditResult.QER_EditOK)
                {
                    if (logger != null)
                    {
                        logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName,
                                          SR.GetString(SR.UpgradeCannotOpenProjectFileForEdit));
                    }
                    return(VSConstants.E_FAIL);
                }

                // If file was modified during the checkout, maybe upgrade is not needed
                if ((queryEditMoreInfo & (uint)tagVSQueryEditResultFlags.QER_MaybeChanged) != 0)
                {
                    this.UpgradeProject_CheckOnly(projectFileName, logger, out upgradeRequired, out newProjectFactory, out ignore);
                    if (upgradeRequired == 0)
                    {
                        if (logger != null)
                        {
                            logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_INFORMATIONAL, projectName, projectFileName,
                                              SR.GetString(SR.UpgradeNoNeedToUpgradeAfterCheckout));
                        }

                        return(VSConstants.S_OK);
                    }
                }
            }

            // Convert the project
            Microsoft.Build.Conversion.ProjectFileConverter projectConverter = new Microsoft.Build.Conversion.ProjectFileConverter();
            projectConverter.OldProjectFile = projectFileName;
            projectConverter.NewProjectFile = projectFileName;
            ProjectRootElement convertedProject = null;

            try
            {
                convertedProject = projectConverter.ConvertInMemory();
            }
            catch (Exception ex)
            {
                if (logger != null)
                {
                    logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName, ex.Message);
                }
            }

            if (convertedProject != null)
            {
                this.m_lastUpgradedProjectFile = projectFileName;
                foreach (ProjectPropertyElement property in convertedProject.Properties)
                {
                    switch (property.Name)
                    {
                    case SCC_LOCAL_PATH:
                        this.m_sccLocalPath = property.Value;
                        break;

                    case SCC_AUX_PATH:
                        this.m_sccAuxPath = property.Value;
                        break;

                    case SCC_PROVIDER:
                        this.m_sccProvider = property.Value;
                        break;

                    case SCC_PROJECT_NAME:
                        this.m_sccProjectName = property.Value;
                        break;

                    default:
                        break;
                    }
                }
                try
                {
                    convertedProject.Save(projectFileName);
                }
                catch (Exception ex)
                {
                    if (logger != null)
                    {
                        logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName, ex.Message);
                    }
                    return(VSConstants.E_FAIL);
                }
                if (logger != null)
                {
                    logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_STATUSMSG, projectName, projectFileName,
                                      SR.GetString(SR.UpgradeSuccessful));
                }
                return(VSConstants.S_OK);
            }

            this.m_lastUpgradedProjectFile = null;
            upgradeFullyQualifiedFileName  = "";
            return(VSConstants.E_FAIL);
        }
        /// <summary>
        /// Called by projects and editors before modifying a file
        /// The function allows the source control systems to take the necessary actions (checkout, flip attributes)
        /// to make the file writable in order to allow the edit to continue
        ///
        /// There are a lot of cases to deal with during QueryEdit/QuerySave.
        /// - called in commmand line mode, when UI cannot be displayed
        /// - called during builds, when save shoudn't probably be allowed
        /// - called during projects migration, when projects are not open and not registered yet with source control
        /// - checking out files may bring new versions from vss database which may be reloaded and the user may lose in-memory changes; some other files may not be reloadable
        /// - not all editors call QueryEdit when they modify the file the first time (buggy editors!), and the files may be already dirty in memory when QueryEdit is called
        /// - files on disk may be modified outside IDE and may have attributes incorrect for their scc status
        /// - checkouts may fail
        /// </summary>
        /// <param name="rgfQueryEdit">The RGF query edit.</param>
        /// <param name="cFiles">The c files.</param>
        /// <param name="rgpszMkDocuments">The RGPSZ mk documents.</param>
        /// <param name="rgrgf">The RGRGF.</param>
        /// <param name="rgFileInfo">The rg file info.</param>
        /// <param name="pfEditVerdict">The pf edit verdict.</param>
        /// <param name="prgfMoreInfo">The PRGF more info.</param>
        /// <returns></returns>
        public int QueryEditFiles(uint rgfQueryEdit, int cFiles, string[] rgpszMkDocuments, uint[] rgrgf, VSQEQS_FILE_ATTRIBUTE_DATA[] rgFileInfo, out uint pfEditVerdict, out uint prgfMoreInfo)
        {
            tagVSQueryEditFlags queryFlags = (tagVSQueryEditFlags)rgfQueryEdit;

            pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK;
            prgfMoreInfo  = (uint)(tagVSQueryEditResultFlags)0; // Must be 0 when verdict is QER_EditOK or you see failures like issue #624

            bool allowUI = (queryFlags & (tagVSQueryEditFlags.QEF_SilentMode | tagVSQueryEditFlags.QEF_ReportOnly | tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting)) == 0;

            bool?allowReadOnlyNonSccWrites = null;

            if (rgpszMkDocuments == null)
            {
                return(VSErr.E_POINTER);
            }

            try
            {
                // If the editor asks us to do anything in our power to make it editable
                // without prompting, just make those files writable.
                if ((queryFlags & tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting) != 0)
                {
                    return(QueryEditForceWritable(rgpszMkDocuments));
                }

                HybridCollection <string> mustLockFiles     = null;
                HybridCollection <string> readOnlyEditFiles = null;
                List <SvnItem>            mustLockItems     = null;
                List <SvnItem>            readOnlyItems     = null;

                for (int i = 0; i < cFiles; i++)
                {
                    SvnItem item;
                    {
                        string file = rgpszMkDocuments[i];

                        if (!IsSafeSccPath(file))
                        {
                            continue; // Skip non scc paths (Includes %TEMP%\*)
                        }
                        item = StatusCache[file];
                    }

                    Monitor.ScheduleDirtyCheck(item);

                    if (item.IsReadOnlyMustLock && !item.IsDirectory)
                    {
                        if (!allowUI)
                        {
                            pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK;
                            prgfMoreInfo  = (uint)(tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc
                                                   | tagVSQueryEditResultFlags.QER_NoisyCheckoutRequired);

                            return(VSErr.S_OK);
                        }

                        if (mustLockItems == null)
                        {
                            mustLockFiles = new HybridCollection <string>(StringComparer.OrdinalIgnoreCase);
                            mustLockItems = new List <SvnItem>();
                        }

                        if (!mustLockFiles.Contains(item.FullPath))
                        {
                            mustLockFiles.Add(item.FullPath);
                            mustLockItems.Add(item);
                        }
                    }
                    else if (item.IsReadOnly)
                    {
                        if (!allowReadOnlyNonSccWrites.HasValue)
                        {
                            allowReadOnlyNonSccWrites = AllowReadOnlyNonSccWrites();
                        }

                        if (!allowReadOnlyNonSccWrites.Value)
                        {
                            if (!allowUI)
                            {
                                pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK;
                                prgfMoreInfo  = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEditNotAllowed
                                                       | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc
                                                       | tagVSQueryEditResultFlags.QER_NoisyPromptRequired);

                                return(VSErr.S_OK);
                            }

                            if (readOnlyEditFiles == null)
                            {
                                readOnlyEditFiles = new HybridCollection <string>(StringComparer.OrdinalIgnoreCase);
                                readOnlyItems     = new List <SvnItem>();
                            }

                            if (!readOnlyEditFiles.Contains(item.FullPath))
                            {
                                readOnlyEditFiles.Add(item.FullPath);
                                readOnlyItems.Add(item);
                            }
                        }
                        // else // allow editting
                    }
                }
                if (mustLockItems != null)
                {
                    List <SvnItem> mustBeLocked = new List <SvnItem>(mustLockItems);

                    // Look at all subfiles of the must be locked document and add these to the dialog
                    // to make it easier to lock them too
                    foreach (string lockFile in new List <string>(mustLockFiles))
                    {
                        foreach (string file in GetAllDocumentFiles(lockFile))
                        {
                            if (!mustLockFiles.Contains(file))
                            {
                                mustLockFiles.Add(file);
                                mustLockItems.Add(StatusCache[file]);
                            }
                        }
                    }

                    CommandService.DirectlyExecCommand(AnkhCommand.SccLock, mustLockItems, CommandPrompt.DoDefault);
                    // Only check the original list; the rest of the items in mustLockItems is optional
                    foreach (SvnItem i in mustBeLocked)
                    {
                        if (i.IsReadOnlyMustLock)
                        {
                            // User has probably canceled the lock operation, or it failed.
                            pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK;
                            prgfMoreInfo  = (uint)(tagVSQueryEditResultFlags.QER_CheckoutCanceledOrFailed
                                                   | tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc);
                            break;
                        }
                    }
                }
                if (readOnlyItems != null)
                {
                    // TODO: Handle multiple items correctly
                    // Is this only for non-scc items or also for scc items that are readonly for other reasons?
                    CommandResult result =
                        CommandService.DirectlyExecCommand(AnkhCommand.MakeNonSccFileWriteable, readOnlyItems[0], CommandPrompt.DoDefault);

                    bool allowed = result.Result is bool?(bool)result.Result : false;

                    if (!allowed)
                    {
                        pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK;
                        prgfMoreInfo  = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEditNotAllowed
                                               | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc // TODO: Specialize to SCC?
                                               );
                    }
                }
            }
            catch (Exception ex)
            {
                IAnkhErrorHandler eh = GetService <IAnkhErrorHandler>();

                if (eh != null && eh.IsEnabled(ex))
                {
                    eh.OnError(ex);
                }
                else
                {
                    throw;
                }
            }

            return(VSErr.S_OK);
        }