private static void DoFetch(ConfLine conf) { if (!string.IsNullOrEmpty(conf.DistantPath)) { var answ = (_dontWarnFetch || _silentUpdate) ? 0 : UserCommunication.Message("This will <b>replace your local</b> configuration with the distant one.<br><br>Do you wish to continue?", MessageImg.MsgInfo, "Fetch", "Confirmation", new List <string> { "Yes I do", "Yes don't ask again", "No, Cancel" }, true); if (answ == 0 || answ == 1) { if (answ == 1) { _dontWarnFetch = true; } if (conf.IsDir) { Utils.CopyDirectory(conf.DistantPath, conf.LocalPath); } else { Utils.CopyFile(conf.DistantPath, conf.LocalPath); if (conf.OnImport != null) { conf.OnImport(conf); } } } } }
/// <summary> /// To call when the user click on an update button /// </summary> public static void CheckForUpdate() { if (!Utils.IsSpamming("updates", 1000)) { UserCommunication.Notify("Now checking for updates, you will be notified when it's done", MessageImg.MsgInfo, "Update", "Update check", 5); Task.Factory.StartNew(() => { CheckForUpdate(true); }); } }
/// <summary> /// Shows a Messagebox informing the user that something went wrong with a file, /// renames said file with the suffix "_errors" /// </summary> public static void ShowErrors(Exception e, string message, string fileName) { UserCommunication.Notify("An error has occurred while loading the following file :<br>" + (fileName + "_errors").ToHtmlLink() + "<br><br>The file has been suffixed with '_errors' to avoid further problems.", MessageImg.MsgPoison, "File load error", message, args => { Npp.Goto(args.Link); args.Handled = true; }); Utils.DeleteFile(fileName + "_errors"); Utils.MoveFile(fileName, fileName + "_errors"); ShowErrors(e, message); // show initial error }
/// <summary> /// check if the User Defined Language for "OpenEdgeABL" exists in the /// userDefineLang.xml file, if it does it updates it, if it doesn't exists it creates it and asks the user /// to restart Notepad++ /// Can also only check and not install it by setting onlyCheckInstall to true /// </summary> public static bool InstallUdl(bool onlyCheckInstall = false) { var encoding = TextEncodingDetect.GetFileEncoding(Config.FileNppUdlXml); var fileContent = File.Exists(Config.FileNppUdlXml) ? Utils.ReadAllText(Config.FileNppUdlXml, encoding) : @"<NotepadPlus />"; var regex = new Regex("<UserLang name=\"OpenEdgeABL\".*?</UserLang>", RegexOptions.Singleline | RegexOptions.IgnoreCase); var matches = regex.Match(fileContent); if (matches.Success) { if (onlyCheckInstall) { return(true); } // if it already exists in the file, delete the existing one fileContent = regex.Replace(fileContent, @""); } else { if (onlyCheckInstall) { return(false); } // if it doesn't exist in the file UserCommunication.Notify("It seems to be the first time that you use this plugin.<br>In order to activate the syntax highlighting, you must restart notepad++.<br><br><i>Please note that if a document is opened at the next start, you will have to manually close/reopen it to see the changes.</i><br><br><b>Sorry for the inconvenience</b>!", MessageImg.MsgInfo, "Information", "Installing syntax highlighting"); } if (fileContent.ContainsFast(@"<NotepadPlus />")) { fileContent = fileContent.Replace(@"<NotepadPlus />", "<NotepadPlus>\r\n" + DataResources.UDL + "\r\n</NotepadPlus>"); } else { fileContent = fileContent.Replace(@"<NotepadPlus>", "<NotepadPlus>\r\n" + DataResources.UDL); } // write to userDefinedLang.xml try { Utils.FileWriteAllText(Config.FileNppUdlXml, fileContent, encoding); } catch (Exception e) { if (e is UnauthorizedAccessException) { UserCommunication.Notify("<b>Couldn't access the file :</b><br>" + Config.FileNppUdlXml + "<br><br>This means i couldn't correctly applied the syntax highlighting feature!<br><br><i>Please make sure to allow write access to this file (Right click on file > Security > Check what's needed to allow total control to current user)</i>", MessageImg.MsgError, "Syntax highlighting", "Can't access userDefineLang.xml"); } else { ErrorHandler.ShowErrors(e, "Error while accessing userDefineLang.xml"); } return(false); } return(true); }
private static void DoDelete(ConfLine conf) { var answ = UserCommunication.Message("Do you really want to delete this file?", MessageImg.MsgQuestion, "Delete", "Confirmation", new List <string> { "Yes I do", "No, Cancel" }, true); if (answ == 0) { Utils.DeleteFile(conf.LocalPath); if (conf.OnImport != null) { conf.OnImport(conf); } } }
/// <summary> /// Shows an error to the user /// </summary> /// <param name="e"></param> /// <param name="message"></param> public static void ShowErrors(Exception e, string message = null) { if (LogError(e, message, false)) { // show it to the user UserCommunication.Notify("The last action you started has triggered an error and has been canceled.<div class='ToolTipcodeSnippet'>" + e.Message + "</div><br>1. If you didn't ask anything from 3P then you can probably ignore this message.<br>2. Otherwise, you might want to check out the error log below for more details :" + (File.Exists(Config.FileErrorLog) ? "<br>" + Config.FileErrorLog.ToHtmlLink("Link to the error log") : "no .log found!") + "<br>Consider opening an issue on GitHub :<br>" + Config.IssueUrl.ToHtmlLink() + "<br><br>If needed, try to restart Notepad++ and see if things are better!</b>", MessageImg.MsgPoison, "An error has occurred", message, args => { if (args.Link.EndsWith(".log")) { Npp.Goto(args.Link); args.Handled = true; } }); } }
/// <summary> /// Try to import the given configuration file /// </summary> public static bool TryToImportFile(string filePath) { if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath)) { var item = List.FirstOrDefault(line => line.HandledItem.Equals(filePath)); if (item != null) { if (item.OnImport != null) { item.OnImport(item); } UserCommunication.NotifyUnique("Importedconf", "The latest changes to <b>" + item.Label + "</b> have been saved and taken into account!", MessageImg.MsgInfo, "Configuration imported", item.Label, null, 5); return(true); } } return(false); }
/// <summary> /// Method to call when the user starts notepad++, /// check if an update has been done since the last time notepad was closed /// </summary> public static void CheckForUpdateDone() { // an update has been done if (File.Exists(Config.FileVersionLog)) { // The dll is still in the update dir, something went wrong if (File.Exists(Config.FileDownloadedPlugin)) { UserCommunication.Notify(@"<h2>I require your attention!</h2><br> <div> The update didn't go as expected, i couldn't replace the old plugin file by the new one!<br> It is very likely because i didn't get the rights to write a file in your /plugins/ folder, don't panic!<br> You will have to manually copy the new file and delete the old file :<br><br> <b>MOVE (delete the source and replace the target)</b> this file : <div>" + Path.GetDirectoryName(Config.FileDownloadedPlugin).ToHtmlLink(Config.FileDownloadedPlugin) + @"</div><br> <b>In this folder</b> (replacing the old file) : <div>" + Path.GetDirectoryName(AssemblyInfo.Location).ToHtmlLink() + @"</div><br><br> Please do it as soon as possible, as i will stop checking for more updates until this problem is fixed.<br> <i>(n.b. : this message will be shown at startup as long as the above-mentionned file exists!)</i><br> Thank you for your patience!</div>", MessageImg.MsgUpdate, "Update", "Problem during the update!"); return; } UserCommunication.Message(("# What's new in this version? #\n\n" + Utils.ReadAllText(Config.FileVersionLog, Encoding.Default)).MdToHtml(), MessageImg.MsgUpdate, "A new version has been installed!", "Updated to version " + AssemblyInfo.Version, new List <string> { "ok" }, false); // delete update related files/folders Utils.DeleteFile(Config.FileVersionLog); Utils.DeleteDirectory(Config.FolderUpdate, true); // reset the log files Utils.DeleteDirectory(Config.FolderLog, true); // update UDL if (!Config.Instance.GlobalDontUpdateUdlOnUpdate) { Style.InstallUdl(); } } }
private static void NotifyUpdateAvailable() { if (_latestReleaseInfo != null) { UserCommunication.NotifyUnique("UpdateAvailable", @"Dear user, <br> <br> A new version of 3P has been downloaded!<br> It will be automatically installed the next time you restart Notepad++<br> <br> Your version: <b>" + AssemblyInfo.Version + @"</b><br> Distant version: <b>" + _latestReleaseInfo.tag_name + @"</b><br> Release name: <b>" + _latestReleaseInfo.name + @"</b><br> Available since: <b>" + _latestReleaseInfo.published_at + @"</b><br>" + "Release URL: <b>" + _latestReleaseInfo.html_url.ToHtmlLink() + @"</b><br>" + (_latestReleaseInfo.prerelease ? "<i>This distant release is a beta version</i><br>" : "") + (_3PUpdater.Instance.IsAdminRightsNeeded ? "<br><span class='SubTextColor'><i><b>3pUpdater.exe</b> will need admin rights to replace your current 3P.dll file by the new release,<br>please click yes when you are asked to execute it</i></span>" : ""), MessageImg.MsgUpdate, "Update check", "An update is available", null); _warnedUserAboutUpdateAvail = true; // stop checking for more updates :) _checkEveryHourAction.Dispose(); } }
private static void DoPush(ConfLine conf) { if (!string.IsNullOrEmpty(conf.LocalPath)) { var answ = _dontWarnPush ? 0 : UserCommunication.Message("This will <b>replace the distant configuration <i>(for everyone!)</i></b> with your local configuration.<br><br>Do you wish to continue?", MessageImg.MsgWarning, "Push", "Confirmation", new List <string> { "Yes I do", "Yes don't ask again", "No, Cancel" }, true); if (answ == 0 || answ == 1) { if (answ == 1) { _dontWarnPush = true; } if (conf.IsDir) { Utils.CopyDirectory(conf.LocalPath, conf.DistantPath); } else { Utils.CopyFile(conf.LocalPath, conf.DistantPath); } } } }
/// <summary> /// Shows an error to the user /// </summary> /// <param name="e"></param> /// <param name="message"></param> public static void ShowErrors(Exception e, string message = null) { if (LogError(e, message)) { if (UserCommunication.Ready) { // show it to the user UserCommunication.Notify("The last action you started has triggered an error and has been cancelled.<div class='ToolTipcodeSnippet'>" + e.Message + "</div><br>1. If you didn't ask anything from 3P then you can probably ignore this message.<br>2. Otherwise, you might want to check out the error log below for more details :" + (File.Exists(Config.FileErrorLog) ? "<br>" + Config.FileErrorLog.ToHtmlLink("Link to the error log") : "no .log found!") + "<br>Consider opening an issue on GitHub :<br>" + Config.IssueUrl.ToHtmlLink() + "<br><br>If needed, try to restart Notepad++ and see if things are better!</b>", MessageImg.MsgPoison, "An error has occured", message, args => { if (args.Link.EndsWith(".log")) { Npp.Goto(args.Link); args.Handled = true; } }); } else { // show an old school message MessageBox.Show("An error has occurred and we couldn't display a notification.\n\nThis very likely happened during the plugin loading; hence there is a hugh probability that it will cause the plugin to not operate normally.\n\nCheck the log at the following location to learn more about this error : " + Config.FileErrorLog.ProQuoter() + "\n\nTry to restart Notepad++, consider opening an issue on : " + Config.IssueUrl + " if the problem persists.", AssemblyInfo.AssemblyProduct + " error message", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
/// <summary> /// Shows a Messagebox informing the user that something went wrong with a file, /// renames said file with the suffix "_errors" /// </summary> public static void ShowErrors(Exception e, string message, string fileName) { if (UserCommunication.Ready) { UserCommunication.Notify("An error has occurred while loading the following file :<div>" + (fileName + "_errors").ToHtmlLink() + "</div><br>The file has been suffixed with '_errors' to avoid further problems.", MessageImg.MsgPoison, "File load error", message, args => { if (args.Link.EndsWith(".log")) { Npp.Goto(args.Link); args.Handled = true; } }); } else { MessageBox.Show("An error has occurred while loading the following file :" + "\n\n" + fileName + "\n\n" + "The file has been suffixed with '_errors' to avoid further problems.", AssemblyInfo.AssemblyProduct + " error message", MessageBoxButtons.OK, MessageBoxIcon.Error); } Utils.DeleteFile(fileName + "_errors"); Utils.MoveFile(fileName, fileName + "_errors"); ShowErrors(e, message); }
/// <summary> /// Called when the latest release download is done /// </summary> /// <param name="sender"></param> /// <param name="asyncCompletedEventArgs"></param> private static void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs asyncCompletedEventArgs) { try { // Extract the .zip file if (Utils.ExtractAll(Config.FileLatestReleaseZip, Config.FolderUpdate)) { // check the presence of the plugin file if (File.Exists(Config.FileDownloadedPlugin)) { // set up the update so the .dll file downloaded replaces the current .dll _3PUpdater.Instance.AddFileToMove(Config.FileDownloadedPlugin, AssemblyInfo.Location); // if the release was containing a .pdb file, we want to copied it as well if (File.Exists(Config.FileDownloadedPdb)) { _3PUpdater.Instance.AddFileToMove(Config.FileDownloadedPdb, Path.Combine(Path.GetDirectoryName(AssemblyInfo.Location) ?? "", Path.GetFileName(Config.FileDownloadedPdb) ?? "")); } // write the version log Utils.FileWriteAllText(Config.FileVersionLog, _latestReleaseInfo.body, Encoding.Default); NotifyUpdateAvailable(); } else { Utils.DeleteDirectory(Config.FolderUpdate, true); } } else { UserCommunication.Notify("I failed to unzip the following file : <br>" + Config.FileLatestReleaseZip + "<br>It contains the update for 3P, you will have to do a manual update.", MessageImg.MsgError, "Unzip", "Failed"); } } catch (Exception e) { ErrorHandler.ShowErrors(e, "On Download File Completed"); } }
/// <summary> /// Update the information of the conf list, using the given share directory /// </summary> public static void UpdateList(string distantShareDirectory) { try { // We get the latest info for each line bool sharedDirOk = false; if (!string.IsNullOrEmpty(distantShareDirectory) && Directory.Exists(distantShareDirectory)) { sharedDirOk = true; Config.Instance.SharedConfFolder = distantShareDirectory; } StringBuilder updateMessage = new StringBuilder(); // update each line of the list foreach (var confLine in List) { // read the autoupdate status from the config confLine.AutoUpdate = Config.Instance.AutoUpdateConfList.ContainsFast(confLine.Label); confLine.LocalPath = confLine.HandledItem; confLine.DistantPath = sharedDirOk ? Path.Combine(distantShareDirectory, (confLine.IsDir ? Path.GetFileName(confLine.HandledItem.TrimEnd('\\')) : Path.GetFileName(confLine.HandledItem)) ?? "") : ""; confLine.LocalTime = DateTime.Now; confLine.DistantTime = DateTime.Now; if (confLine.IsDir) { confLine.LocalExists = Directory.Exists(confLine.LocalPath); confLine.DistantExists = !string.IsNullOrEmpty(confLine.DistantPath) && Directory.Exists(confLine.DistantPath); if (confLine.LocalExists) { confLine.LocalNbFiles = 0; foreach (var file in Directory.GetFiles(confLine.LocalPath)) { if (confLine.LocalNbFiles == 0) { confLine.LocalTime = File.GetLastWriteTime(file); } else if (File.GetLastWriteTime(file).CompareTo(confLine.LocalTime) > 0) { confLine.LocalTime = File.GetLastWriteTime(file); } confLine.LocalNbFiles++; } } if (!string.IsNullOrEmpty(confLine.DistantPath) && confLine.DistantExists) { confLine.DistantNbFiles = 0; foreach (var file in Directory.GetFiles(confLine.DistantPath)) { if (confLine.DistantNbFiles == 0) { confLine.DistantTime = File.GetLastWriteTime(file); } else if (File.GetLastWriteTime(file).CompareTo(confLine.DistantTime) > 0) { confLine.DistantTime = File.GetLastWriteTime(file); } confLine.DistantNbFiles++; } } } else { confLine.LocalExists = !string.IsNullOrEmpty(confLine.LocalPath) && File.Exists(confLine.LocalPath); confLine.DistantExists = !string.IsNullOrEmpty(confLine.DistantPath) && File.Exists(confLine.DistantPath); if (confLine.LocalExists) { confLine.LocalTime = File.GetLastWriteTime(confLine.LocalPath); } if (!string.IsNullOrEmpty(confLine.DistantPath) && confLine.DistantExists) { confLine.DistantTime = File.GetLastWriteTime(confLine.DistantPath); } } // if the difference between the two dates are small, correct it (it sometimes happen, even when the files are strictly identical) if (Math.Abs(confLine.LocalTime.Subtract(confLine.DistantTime).TotalSeconds) < 2) { confLine.LocalTime = confLine.DistantTime; } confLine.NeedUpdate = confLine.OnFetch != null && ((confLine.DistantExists && !confLine.LocalExists) || (confLine.LocalExists && confLine.DistantExists && confLine.DistantTime.CompareTo(confLine.LocalTime) > 0)); // the line needs to be autoupdated if (confLine.AutoUpdate && confLine.NeedUpdate && confLine.OnFetch != null) { _silentUpdate = true; confLine.OnFetch(confLine); confLine.LocalExists = true; confLine.LocalTime = confLine.DistantTime; confLine.LocalNbFiles = confLine.DistantNbFiles; confLine.NeedUpdate = false; _silentUpdate = false; if (updateMessage.Length == 0) { updateMessage.Append("The following configuration files have been updated from the shared folder:<br><br>"); } updateMessage.Append("<div><b>" + confLine.Label + "</b></div>"); } } if (updateMessage.Length > 0) { updateMessage.Append("<br><br><i>You can set which config file gets auto-updated in <a href='go'>the option page</a></i>"); UserCommunication.NotifyUnique("ExportConfUpdate", updateMessage.ToString(), MessageImg.MsgInfo, "Update notification", "Configuration auto-update", args => { Appli.Appli.GoToPage(PageNames.ExportShareConf); UserCommunication.CloseUniqueNotif("ExportConfUpdate"); args.Handled = true; }, 10); } } catch (Exception e) { ErrorHandler.ShowErrors(e, "Error while fetching info on the distant files"); } }
/// <summary> /// Called when the gitub api for releases responses /// </summary> private static void WbOnOnRequestEnded(WebServiceJson webServiceJson, bool alwaysGetFeedBack) { try { if (webServiceJson.StatusCodeResponse == HttpStatusCode.OK && webServiceJson.ResponseException == null) { Config.Instance.LastCheckUpdateOk = true; // get the releases var releases = webServiceJson.DeserializeArray <ReleaseInfo>(); if (releases != null && releases.Count > 0) { // sort by descring order releases.Sort((o, o2) => o.tag_name.IsHigherVersionThan(o2.tag_name) ? -1 : 1); var localVersion = AssemblyInfo.Version; var outputBody = new StringBuilder(); foreach (var release in releases) { if (string.IsNullOrEmpty(release.tag_name)) { continue; } // For each version higher than the local one, append to the release body // Will be used to display the version log to the user if (release.tag_name.IsHigherVersionThan(localVersion) && (Config.Instance.UserGetsPreReleases || !release.prerelease) && release.assets != null && release.assets.Count > 0 && release.assets.Exists(asset => asset.name.EqualsCi(Config.FileGitHubAssetName))) { // in case something is undefined (but shouldn't happen) if (string.IsNullOrEmpty(release.tag_name)) { release.tag_name = "vX.X.X.X"; } if (string.IsNullOrEmpty(release.name)) { release.name = "unknown"; } if (string.IsNullOrEmpty(release.body)) { release.body = "..."; } if (string.IsNullOrEmpty(release.published_at)) { release.published_at = DateTime.Now.ToString(CultureInfo.CurrentCulture); } // h1 outputBody.AppendLine("## " + release.tag_name + " : " + release.name + " ##\n\n"); // body outputBody.AppendLine(release.body + "\n\n"); // the first higher release encountered is the latest if (_latestReleaseInfo == null) { _latestReleaseInfo = release; } } } // There is a distant version higher than the local one if (_latestReleaseInfo != null) { // to display all the release notes _latestReleaseInfo.body = outputBody.ToString(); // delete existing dir Utils.DeleteDirectory(Config.FolderUpdate, true); Utils.DownloadFile(_latestReleaseInfo.assets.First(asset => asset.name.EqualsCi(Config.FileGitHubAssetName)).browser_download_url, Config.FileLatestReleaseZip, OnDownloadFileCompleted); } else if (alwaysGetFeedBack) { UserCommunication.NotifyUnique("UpdateChecked", "Congratulations! You already possess the latest <b>" + (!Config.Instance.UserGetsPreReleases && !AssemblyInfo.IsPreRelease ? "stable" : "beta") + "</b> version of 3P!", MessageImg.MsgOk, "Update check", "You own the version " + AssemblyInfo.Version, null); } } } else { // failed to retrieve the list if (alwaysGetFeedBack || Config.Instance.LastCheckUpdateOk) { UserCommunication.NotifyUnique("ReleaseListDown", "For your information, I couldn't manage to retrieve the latest published version on github.<br><br>A request has been sent to :<br>" + Config.ReleasesApi.ToHtmlLink() + "<br>but was unsuccessul, you might have to check for a new version manually if this happens again.", MessageImg.MsgHighImportance, "Couldn't reach github", "Connection failed", null); } Config.Instance.LastCheckUpdateOk = false; // check if there is an update available in the Shared config folder if (!string.IsNullOrEmpty(Config.Instance.SharedConfFolder) && Directory.Exists(Config.Instance.SharedConfFolder)) { var potentialUpdate = Path.Combine(Config.Instance.SharedConfFolder, AssemblyInfo.AssemblyName); // if the .dll exists, is higher version and (the user get beta releases or it's a stable release) if (File.Exists(potentialUpdate) && Utils.GetDllVersion(potentialUpdate).IsHigherVersionThan(AssemblyInfo.Version) && (Config.Instance.UserGetsPreReleases || Utils.GetDllVersion(potentialUpdate).EndsWith(".0"))) { // copy to local update folder and warn the user if (Utils.CopyFile(potentialUpdate, Config.FileDownloadedPlugin)) { _latestReleaseInfo = new ReleaseInfo { name = "Updated from shared directory", tag_name = Utils.GetDllVersion(Config.FileDownloadedPlugin), prerelease = Utils.GetDllVersion(Config.FileDownloadedPlugin).EndsWith(".1"), published_at = "???", html_url = Config.UrlCheckReleases }; // write the version log Utils.FileWriteAllText(Config.FileVersionLog, @"This version has been updated from the shared directory" + Environment.NewLine + Environment.NewLine + @"Find more information on this release [here](" + Config.UrlCheckReleases + @")", Encoding.Default); // set up the update so the .dll file downloaded replaces the current .dll _3PUpdater.Instance.AddFileToMove(Config.FileDownloadedPlugin, AssemblyInfo.Location); NotifyUpdateAvailable(); } } } } } catch (Exception e) { ErrorHandler.ShowErrors(e, "Error when checking the latest release "); } _checking = false; }