private void RefreshEmail()
 {
     try {
         string text = MarkupEdit.TranslateToXhtml(_emailPlainTextShared, isPreviewOnly: true, hasWikiPageTitles: false, isEmail: true);
         browserEmailBody.DocumentText = text;
     }
     catch (Exception ex) {
         ex.DoNothing();
     }
 }
Esempio n. 2
0
 private void LoadWikiPage(WikiPage WikiPageCur)
 {
     try {
         textContent.Text            = WikiPages.GetWikiPageContentWithWikiPageTitles(WikiPageCur.PageContent);
         webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(textContent.Text, false, hasWikiPageTitles: true);
     }
     catch (Exception ex) {
         webBrowserWiki.DocumentText = "";
         MessageBox.Show(this, Lan.g(this, "This page is broken and cannot be viewed.  Error message:") + " " + ex.Message);
     }
 }
 ///<summary>Refreshes the email preview pane to show the web browser when viewing HTML and the editable text if not.</summary>
 public void ChangeView(bool isHtml)
 {
     if (_hasTextChanged)
     {
         //plain text box changed, grab the new plain text string and translate into html, regardless of if this is currently an html message or not.
         try {
             string htmlText = "";
             if (_isRaw)
             {
                 htmlText      = textBodyText.Text;
                 _htmlDocument = htmlText;
             }
             else
             {
                 //handle aggregation of the full document text with the template ourselves so we can display properly but only save the html string.
                 htmlText      = MarkupEdit.TranslateToXhtml(textBodyText.Text, false, false, true, false);
                 _htmlDocument = PrefC.GetString(PrefName.EmailMasterTemplate).Replace("@@@body@@@", htmlText);
             }
             _hasTextChanged = false;
         }
         catch (Exception ex) {
             FriendlyException.Show("Error loading HTML.", ex);
         }
     }
     if (isHtml || _isRaw)
     {
         try {
             textBodyText.Visible        = false;
             webBrowserHtml.Visible      = true;
             webBrowserHtml.Location     = textBodyText.Location;
             webBrowserHtml.Size         = textBodyText.Size;
             webBrowserHtml.Anchor       = textBodyText.Anchor;
             webBrowserHtml.DocumentText = _htmlDocument;
             webBrowserHtml.BringToFront();
         }
         catch (Exception ex) {
             ex.DoNothing();
             //invalid preview
         }
     }
     else
     {
         //load the plain text
         webBrowserHtml.Visible = false;
         textBodyText.Visible   = true;
         textBodyText.BringToFront();
     }
 }
Esempio n. 4
0
 private void LoadWikiPage(WikiPageHist wikiPageCur)
 {
     try {
         if (string.IsNullOrEmpty(wikiPageCur.PageContent))
         {
             //if this is the first time the user has clicked on this revision, get page content from db (the row's tag will have this as well)
             wikiPageCur.PageContent = WikiPageHists.GetPageContent(wikiPageCur.WikiPageNum);
         }
         textContent.Text            = WikiPages.GetWikiPageContentWithWikiPageTitles(wikiPageCur.PageContent);
         webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(textContent.Text, false, hasWikiPageTitles: true);
     }
     catch (Exception ex) {
         webBrowserWiki.DocumentText = "";
         MessageBox.Show(this, Lan.g(this, "This page is broken and cannot be viewed.  Error message:") + " " + ex.Message);
     }
 }
Esempio n. 5
0
 private void RefreshHtml()
 {
     webBrowserWiki.AllowNavigation = true;
     try {
         //remember scroll
         if (webBrowserWiki.Document != null)
         {
             ScrollTop = webBrowserWiki.Document.GetElementsByTagName("HTML")[0].ScrollTop;
         }
         webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(textContent.Text, true, true);
         _isInvalidPreview           = false;
     }
     catch (Exception ex) {
         ex.DoNothing();
         _isInvalidPreview = true;
         //don't refresh
     }
     //textContent.Focus();//this was causing a bug where it would re-highlight text after a backspace.
 }
Esempio n. 6
0
 private void LoadWikiPage(string WikiPageTitleCur)
 {
     webBrowserWiki.AllowNavigation = true;
     butRestore.Enabled             = false;
     try {
         if (checkArchivedOnly.Checked)
         {
             webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(WikiPages.GetByTitle(WikiPageTitleCur, isDeleted: true).PageContent, true);
             butRestore.Enabled          = true;
         }
         else
         {
             webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(WikiPages.GetByTitle(WikiPageTitleCur).PageContent, true);
         }
     }
     catch (Exception ex) {
         webBrowserWiki.DocumentText = "";
         MessageBox.Show(this, Lan.g(this, "This page is broken and cannot be viewed.  Error message:") + " " + ex.Message);
     }
 }
Esempio n. 7
0
 private void RefreshHTML()
 {
     webBrowserEmail.AllowNavigation = true;
     _isLoading = true;
     try {
         var text = "";
         if (checkIsRaw.Checked)
         {
             text = textContentEmail.Text;
         }
         else
         {
             text = MarkupEdit.TranslateToXhtml(textContentEmail.Text, true, false, true);
             AreImagesDownloaded = true;
         }
         webBrowserEmail.DocumentText = text;
         _isInvalidPreview            = false;
     }
     catch (Exception ex) {
         ex.DoNothing();
         _isInvalidPreview = true;
     }
 }
Esempio n. 8
0
        private void FillMessageThread()
        {
            List <WebChatMessage>   listWebChatMessages = WebChatMessages.GetAllForSessions(_webChatSession.WebChatSessionNum);
            List <SmsThreadMessage> listThreadMessages  = new List <SmsThreadMessage>();

            foreach (WebChatMessage webChatMessage in listWebChatMessages)
            {
                string strMsg = webChatMessage.MessageText;
                if (webChatMessage.MessageType == WebChatMessageType.EndSession)
                {
                    strMsg = MarkupEdit.ConvertToPlainText(strMsg);
                }
                SmsThreadMessage msg = new SmsThreadMessage(webChatMessage.DateT,
                                                            strMsg,
                                                            (webChatMessage.MessageType == WebChatMessageType.Customer),
                                                            false,
                                                            false,
                                                            webChatMessage.UserName
                                                            );
                listThreadMessages.Add(msg);
            }
            webChatThread.ListSmsThreadMessages = listThreadMessages;
        }
		///<summary>Loads the given emailMessage into the control.
		///Set listEmailMessages to messages to be considered for the auto complete contacts pop up.  When null will query.</summary>
    public void LoadEmailMessage(EmailMessage emailMessage,List<EmailMessage> listHistoricEmailMessages=null) {
			Cursor=Cursors.WaitCursor;
			_emailMessage=emailMessage;
			_patCur=Patients.GetPat(_emailMessage.PatNum);//we could just as easily pass this in.
			if(_emailMessage.SentOrReceived==EmailSentOrReceived.Neither) {//Composing a message
				_isComposing=true;
				if(_isSigningEnabled) {
					SetSig(EmailMessages.GetCertFromPrivateStore(_emailMessage.FromAddress));
				}
				_emailMessage.UserNum=Security.CurUser.UserNum;//UserNum is also updated when sent. Setting here to display when composing.
			}
			else {//sent or received (not composing)
				//For all email received or sent types, we disable most of the controls and put the window into a mostly read-only state.
				//There is no reason a user should ever edit a received message.
				//The user can copy the content and send a new email if needed (to mimic forwarding until we add the forwarding feature).
				_isComposing=false;
				textMsgDateTime.Text=_emailMessage.MsgDateTime.ToString();
				textMsgDateTime.ForeColor=Color.Black;
				gridAttachments.SetAddButtonEnabled(false);
				textFromAddress.ReadOnly=true;
				textToAddress.ReadOnly=true;
				textCcAddress.ReadOnly=true;
				textBccAddress.ReadOnly=true;
				textSubject.ReadOnly=true;
				textSubject.SpellCheckIsEnabled=false;//Prevents slowness resizing the window, because spell checker runs each time resize event is fired.
				textBodyText.ReadOnly=true;
				textBodyText.SpellCheckIsEnabled=false;//Prevents slowness resizing the window, because spell checker runs each time resize event is fired.
				butAccountPicker.Visible=false;
				textFromAddress.Width=textCcAddress.Width;//Match the size of Cc Address.
				textFromAddress.Anchor=((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
					| System.Windows.Forms.AnchorStyles.Right)));//Change the anchors to accommodate.
			}
			textSentOrReceived.Text=_emailMessage.SentOrReceived.ToString();
			textFromAddress.Text=_emailMessage.FromAddress;
			textToAddress.Text=_emailMessage.ToAddress;
			textCcAddress.Text=_emailMessage.CcAddress;
			textBccAddress.Text=_emailMessage.BccAddress; //if you send an email to yourself, you'll be able to see everyone in the bcc field.
			textSubject.Text=_emailMessage.Subject;
			textBodyText.Visible=true;
			webBrowser.Visible=false;
			if(EmailMessages.IsReceived(_emailMessage.SentOrReceived)) {
				List<List<MimeEntity>> listMimeParts=
					EmailMessages.GetMimePartsForMimeTypes(_emailMessage.RawEmailIn,EmailAddressPreview,
					"text/html","text/plain","image/","application/octet-stream");//Adding application/octet-stream as sometimes images are sent in this format
				List<MimeEntity> listHtmlParts=listMimeParts[0];//If RawEmailIn is blank, then this list will also be blank (ex Secure Web Mail messages).
				List<MimeEntity> listTextParts=listMimeParts[1];//If RawEmailIn is blank, then this list will also be blank (ex Secure Web Mail messages).
				_listImageParts=listMimeParts[2];//If RawEmailIn is blank, then this list will also be blank (ex Secure Web Mail messages).
				_listAppParts=listMimeParts[3];//If RawEmailIn is blank, then this list will also be blank (ex Secure Web Mail messages).
				if(listHtmlParts.Count>0) {//Html body found.
					textBodyText.Visible=false;
					_isLoading=true;
					try {
						webBrowser.DocumentText=EmailMessages.ProcessMimeTextPart(listHtmlParts[0]);
					}
					catch(ApplicationException ex) {
						webBrowser.DocumentText="Improperly formatted email. Error displaying email: "+ex.Message;
					}
					webBrowser.Location=textBodyText.Location;
					webBrowser.Size=textBodyText.Size;
					webBrowser.Anchor=textBodyText.Anchor;
					webBrowser.Visible=true;
					if(!_listImageParts.IsNullOrEmpty() || !_listAppParts.IsNullOrEmpty()) {
						butShowImages.Visible=true;
					}
				}
				else if(listTextParts.Count>0) {//No html body found, however one specific mime part is for viewing in text only.					
					textBodyText.Text=EmailMessages.ProcessMimeTextPart(listTextParts[0]);
				}
				else {//No html body found and no text body found.  Last resort.  Show all mime parts which are not attachments (ugly).
					textBodyText.Text=_emailMessage.BodyText;//This version of the body text includes all non-attachment mime parts.
				}
				lableUserName.Visible=false;
				textUserName.Visible=false;
			}
			else {//Sent or Unsent/Saved.
				textBodyText.Text=_emailMessage.BodyText;//Show the body text exactly as typed by the user.
				if(EmailMessages.IsHtmlEmail(_emailMessage.HtmlType)) {
					try {
						if(_emailMessage.HtmlType==EmailType.RawHtml) {
							HtmlText=textBodyText.Text;
						}
						else {
							HtmlText=MarkupEdit.TranslateToXhtml(textBodyText.Text,false,false,true);//show the user's text in web browswer with HTML
						}
						InitializeHtmlEmail();//web browser needs to be set and made visible to show the translated text.
					}
					catch(Exception ex) {
						FriendlyException.Show("Error loading HTML.",ex);
					}
				}
				lableUserName.Visible=true;
				textUserName.Visible=true;
				textUserName.Text=(Userods.GetName(_emailMessage.UserNum));//Blank if 0.
			}
			FillAttachments();
			if(IsComposing) {
				LoadEmailAddresses(ClinicNum);
				LoadSig();
				SetHistoricContacts(listHistoricEmailMessages);
			}
			textBodyText.Select();
			Cursor=Cursors.Default;
			if(_isPreview) {
				tabAttachmentsShowEmail.TabPages.Remove(tabShowEmail); //Do not show Hide Email tab when in Email Preview mode
			}
			else {
				InitEmailShowInListBox();
				RefreshShowIn();
			}	
		}
Esempio n. 10
0
        ///<summary>Validates content, and keywords.  isForSaving can be false if just validating for refresh.</summary>
        public static bool ValidateMarkup(ODcodeBox textContent, bool isForSaving, bool showMsgBox = true, bool isEmail = false)
        {
            MatchCollection matches;
            //xml validation----------------------------------------------------------------------------------------------------
            string s = textContent.Text;

            //"<",">", and "&"-----------------------------------------------------------------------------------------------------------
            s = s.Replace("&", "&amp;");
            s = s.Replace("&amp;<", "&lt;");         //because "&" was changed to "&amp;" in the line above.
            s = s.Replace("&amp;>", "&gt;");         //because "&" was changed to "&amp;" in the line above.
            s = "<body>" + s + "</body>";
            XmlDocument  doc    = new XmlDocument();
            StringReader reader = new StringReader(s);

            try {
                doc.Load(reader);
            }
            catch (Exception ex) {
                if (showMsgBox)
                {
                    MessageBox.Show(ex.Message);
                }
                return(false);
            }
            try{
                //we do it this way to skip checking the main node itself since it's a dummy node.
                if (!isEmail)                 //We are allowing any XHTML markup in emails.
                {
                    MarkupEdit.ValidateNodes(doc.DocumentElement.ChildNodes);
                }
            }
            catch (Exception ex) {
                if (showMsgBox)
                {
                    MessageBox.Show(ex.Message);
                }
                return(false);
            }
            //Cannot have CR within tag definition---------------------------------------------------------------------------------
            //(?<!&) means only match strings that do not start with an '&'. This is so we can continue to use '&' as an escape character for '<'.
            //<.*?> means anything as short as possible that is contained inside a tag
            MatchCollection tagMatches = Regex.Matches(textContent.Text, "(?<!&)<.*?>", RegexOptions.Singleline);

            for (int i = 0; i < tagMatches.Count; i++)
            {
                if (tagMatches[i].ToString().Contains("\n"))
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(tagMatches[i].Index) + " - "
                                        + Lans.g(_lanThis, "Tag definitions cannot contain a return line:") + " " + tagMatches[i].Value.Replace("\n", ""));
                    }
                    return(false);
                }
            }
            //wiki image validation-----------------------------------------------------------------------------------------------------
            if (!isEmail)
            {
                string wikiImagePath = "";
                try {
                    wikiImagePath = WikiPages.GetWikiPath();                  //this also creates folder if it's missing.
                }
                catch (Exception ex) {
                    ex.DoNothing();
                    //do nothing, the wikiImagePath is only important if the user adds an image to the wiki page and is checked below
                }
                matches = Regex.Matches(textContent.Text, @"\[\[(img:).*?\]\]");             // [[img:myimage.jpg]]
                if (matches.Count > 0 && PrefC.AtoZfolderUsed == DataStorageType.InDatabase)
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[0].Index) + " - "
                                        + Lans.g(_lanThis, "Cannot use images in wiki if storing images in database."));
                    }
                    return(false);
                }
                if (isForSaving)
                {
                    for (int i = 0; i < matches.Count; i++)
                    {
                        string imgPath = FileAtoZ.CombinePaths(wikiImagePath, matches[i].Value.Substring(6).Trim(']'));
                        if (!FileAtoZ.Exists(imgPath))
                        {
                            if (showMsgBox)
                            {
                                MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[i].Index) + " - "
                                                + Lans.g(_lanThis, "Not allowed to save because image does not exist:") + " " + imgPath);
                            }
                            return(false);
                        }
                    }
                }
            }
            //Email image validation----------------------------------------------------------------------------------------------
            if (isEmail)
            {
                string emailImagePath = "";
                try {
                    emailImagePath = ImageStore.GetEmailImagePath();
                }
                catch (Exception ex) {
                    ex.DoNothing();
                }
                matches = Regex.Matches(textContent.Text, @"\[\[(img:).*?\]\]");
                if (isForSaving)
                {
                    for (int i = 0; i < matches.Count; i++)
                    {
                        string imgPath = FileAtoZ.CombinePaths(emailImagePath, matches[i].Value.Substring(6).Trim(']'));
                        if (!FileAtoZ.Exists(imgPath))
                        {
                            if (showMsgBox)
                            {
                                MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(matches[i].Index) + " - "
                                                + Lans.g(_lanThis, "Not allowed to save because image does not exist: ") + " " + imgPath);
                            }
                        }
                    }
                }
            }
            //List validation-----------------------------------------------------------------------------------------------------
            matches = Regex.Matches(textContent.Text, @"\[\[(list:).*?\]\]");         // [[list:CustomList]]
            foreach (Match match in matches)
            {
                if (!WikiLists.CheckExists(match.Value.Substring(7).Trim(']')))
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                        + Lans.g(_lanThis, "Wiki list does not exist in database:") + " " + match.Value.Substring(7).Trim(']'));
                    }
                    return(false);
                }
            }
            //spacing around bullets-----------------------------------------------------------------------------------------------
            string[] lines = textContent.Text.Split(new string[] { "\n" }, StringSplitOptions.None);
            for (int i = 0; i < lines.Length; i++)
            {
                if (lines[i].Trim().StartsWith("*"))
                {
                    if (!lines[i].StartsWith("*"))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - "
                                            + Lans.g(_lanThis, "Stars used for lists may not have a space before them."));
                        }
                        return(false);
                    }
                    if (lines[i].Trim().StartsWith("* "))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - "
                                            + Lans.g(_lanThis, "Stars used for lists may not have a space after them."));
                        }
                        return(false);
                    }
                }
                if (lines[i].Trim().StartsWith("#"))
                {
                    if (!lines[i].StartsWith("#"))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - "
                                            + Lans.g(_lanThis, "Hashes used for lists may not have a space before them."));
                        }
                        return(false);
                    }
                    if (lines[i].Trim().StartsWith("# "))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + (i + 1) + " - "
                                            + Lans.g(_lanThis, "Hashes used for lists may not have a space after them."));
                        }
                        return(false);
                    }
                }
            }
            //Invalid characters inside of various tags--------------------------------------------
            matches = Regex.Matches(textContent.Text, @"\[\[.*?\]\]");
            foreach (Match match in matches)
            {
                if (match.Value.Contains("\"") && !match.Value.StartsWith("[[color:") && !match.Value.StartsWith("[[font:"))                 //allow colored text to have quotes.
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                        + Lans.g(_lanThis, "Link cannot contain double quotes:") + " " + match.Value);
                    }
                    return(false);
                }
                //This is not needed because our regex doesn't even catch them if the span a line break.  It's just interpreted as plain text.
                //if(match.Value.Contains("\r") || match.Value.Contains("\n")) {
                //	MessageBox.Show(Lan.g(this,"Link cannot contain carriage returns: ")+match.Value);
                //	return false;
                //}
                if (match.Value.StartsWith("[[img:") ||
                    match.Value.StartsWith("[[keywords:") ||
                    match.Value.StartsWith("[[file:") ||
                    match.Value.StartsWith("[[folder:") ||
                    match.Value.StartsWith("[[list:") ||
                    match.Value.StartsWith("[[color:") ||
                    match.Value.StartsWith("[[font:"))
                {
                    //other tags
                }
                else
                {
                    if (match.Value.Contains("|"))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                            + Lans.g(_lanThis, "Internal link cannot contain a pipe character:") + " " + match.Value);
                        }
                        return(false);
                    }
                }
            }
            //Table markup rigorously formatted----------------------------------------------------------------------
            //{|
            //!Width="100"|Column Heading 1!!Width="150"|Column Heading 2!!Width="75"|Column Heading 3
            //|-
            //|Cell 1||Cell 2||Cell 3
            //|-
            //|Cell A||Cell B||Cell C
            //|}
            //Although rarely needed, it might still come in handy in certain cases, like paste, or when user doesn't add the |} until later, and other hacks.
            matches = Regex.Matches(s, @"\{\|\n.+?\n\|\}", RegexOptions.Singleline);
            //matches = Regex.Matches(textContent.Text,
            //	@"(?<=(?:\n|<body>))" //Checks for preceding newline or beggining of file
            //	+@"\{\|.+?\n\|\}" //Matches the table markup.
            //	+@"(?=(?:\n|</body>))" //Checks for following newline or end of file
            //	,RegexOptions.Singleline);
            foreach (Match match in matches)
            {
                lines = match.Value.Split(new string[] { "{|\n", "\n|-\n", "\n|}" }, StringSplitOptions.RemoveEmptyEntries);
                if (!lines[0].StartsWith("!"))
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                        + Lans.g(_lanThis, "The second line of a table markup section must start with ! to indicate column headers."));
                    }
                    return(false);
                }
                if (lines[0].StartsWith("! "))
                {
                    if (showMsgBox)
                    {
                        MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                        + Lans.g(_lanThis, "In the table, at line 2, there cannot be a space after the first !"));
                    }
                    return(false);
                }
                string[] cells = lines[0].Substring(1).Split(new string[] { "!!" }, StringSplitOptions.None);             //this also strips off the leading !
                for (int c = 0; c < cells.Length; c++)
                {
                    if (!Regex.IsMatch(cells[c], @"^(Width="")\d+""\|"))                    //e.g. Width="90"|
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Error at line:") + " " + textContent.GetLineFromCharIndex(match.Index) + " - "
                                            + Lans.g(_lanThis, "In the table markup, each header must be formatted like this: Width=\"#\"|..."));
                        }
                        return(false);
                    }
                }
                for (int i = 1; i < lines.Length; i++)           //loop through the lines after the header
                {
                    if (!lines[i].StartsWith("|"))
                    {
                        if (showMsgBox)
                        {
                            MessageBox.Show(Lans.g(_lanThis, "Table rows must start with |.  At line ") + (i + 1).ToString() + Lans.g(_lanThis, ", this was found instead:")
                                            + lines[i]);
                        }
                        return(false);
                    }
                }
            }
            return(true);
        }
Esempio n. 11
0
        ///<summary>Before calling this, make sure to increment/decrement the historyNavBack index to keep track of the position in history.  If loading a new page, decrement historyNavBack before calling this function.  </summary>
        private void LoadWikiPage(string pageTitle)
        {
            //This is called from 11 different places, any time the program needs to refresh a page from the db.
            //It's also called from the browser_Navigating event when a "wiki:" link is clicked.
            WikiPage wpage = WikiPages.GetByTitle(pageTitle);

            if (wpage == null)
            {
                string errorMsg = "";
                if (!WikiPages.IsWikiPageTitleValid(pageTitle, out errorMsg))
                {
                    MsgBox.Show(this, "That page does not exist and cannot be made because the page title contains invalid characters.");
                    return;
                }
                if (!MsgBox.Show(this, MsgBoxButtons.YesNo, "That page does not exist. Would you like to create it?"))
                {
                    return;
                }
                Action <string> onWikiSaved = new Action <string>((pageTitleNew) => {
                    //Insert the pageTitleNew where the pageTitle exists in the existing WikiPageCur
                    //(guaranteed to be unique because all INVALID WIKIPAGE LINK's have an ID at the end)
                    string pageContent      = WikiPages.GetWikiPageContentWithWikiPageTitles(WikiPageCur.PageContent);
                    WikiPageCur.PageContent = pageContent.Replace(pageTitle, pageTitleNew);
                    WikiPageCur.PageContent = WikiPages.ConvertTitlesToPageNums(WikiPageCur.PageContent);
                    if (!WikiPageCur.IsDraft)
                    {
                        WikiPageCur.PageContentPlainText = MarkupEdit.ConvertToPlainText(WikiPageCur.PageContent);
                    }
                    WikiPages.Update(WikiPageCur);
                });
                AddWikiPage(onWikiSaved);
                return;
            }
            WikiPageCur = wpage;
            try {
                webBrowserWiki.DocumentText = MarkupEdit.TranslateToXhtml(WikiPageCur.PageContent, false);
            }
            catch (Exception ex) {
                webBrowserWiki.DocumentText = "";
                MessageBox.Show(this, Lan.g(this, "This page is broken and cannot be viewed.  Error message:") + " " + ex.Message);
            }
            Text = "Wiki - " + WikiPageCur.PageTitle;
            #region historyMaint
            //This region is duplicated in webBrowserWiki_Navigating() for external links.  Modifications here will need to be reflected there.
            int indexInHistory = historyNav.Count - (1 + historyNavBack); //historyNavBack number of pages before the last page in history.  This is the index of the page we are loading.
            if (historyNav.Count == 0)                                    //empty history
            {
                historyNavBack = 0;
                historyNav.Add("wiki:" + pageTitle);
            }
            else if (historyNavBack < 0)           //historyNavBack could be negative here.  This means before the action that caused this load, we were not navigating through history, simply set back to 0 and add to historyNav[] if necessary.
            {
                historyNavBack = 0;
                if (historyNav[historyNav.Count - 1] != "wiki:" + pageTitle)
                {
                    historyNav.Add("wiki:" + pageTitle);
                }
            }
            else if (historyNavBack >= 0 && historyNav[indexInHistory] != "wiki:" + pageTitle) //branching from page in history
            {
                historyNav.RemoveRange(indexInHistory, historyNavBack + 1);                    //remove "forward" history. branching off in a new direction
                historyNavBack = 0;
                historyNav.Add("wiki:" + pageTitle);
            }
            #endregion
        }